【动态规划】背包模型

以下题目、题解思路等等均来自AcWing算法提高课,感兴趣的同学欢迎点击下方链接了解一下。
https://www.acwing.com/activity/content/introduction/16/

一、01背包及各种变形

A、AcWing 423. 采药

在这里插入图片描述
01背包模板

#include<cstdio>
#include<algorithm>

using namespace std;

const int N = 50007;

int n, m;
int f[N];
int t[N], w[N];

int main()
{
    scanf("%d%d", &m, &n);
    for(int i = 1; i <= n; ++ i)
    {
        scanf("%d%d", &t[i], &w[i]);
    }
    
    for(int i = 1; i <= n; ++ i)
    for(int j = m; j >= t[i]; -- j)
    f[j] = max(f[j], f[j - t[i]] + w[i]);
    printf("%d\n", f[m]);
    return 0;
}

B、AcWing 1024. 装箱问题

在这里插入图片描述
实际上可以直接看做01背包来做,题目要求求出最小的剩余空间,也就是要求出最大的可装体积
我们可以把体积看成是价值,这样问题就转变为了求最大价值。

#include<iostream>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 50007, INF = 0x3f3f3f3f;

int n, v;
int w[N];
int f[N];
int main()
{
    cin >> v >> n;
    
    for(int i = 1; i <= n; ++ i)
        cin >> w[i];
    for(int i = 1; i <= n; ++ i)
        for(int j = v; j >= w[i]; -- j)
        {
            f[j] = max(f[j], f[j - w[i]] + w[i]);
        }
    cout << v - f[v] << endl;
}

C、AcWing 278. 数字组合(01背包模板求总方案数)

在这里插入图片描述
每个数只能选一次,01背包模板求总方案数。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>

using namespace std;

const int N = 50007;

int n, m;
int f[N];
int a[N];

int main(){
    cin >> n >> m;
    for(int i = 1;i <= n; ++ i){
        cin >> a[i];
    }
    
    f[0] = 1;
    
    for(int i = 1;i <= n; ++ i)
    for(int j = m; j >= a[i]; -- j){
        f[j] += f[j - a[i]];
    }
    
    cout << f[m] << endl;
    return 0;
}

D、AcWing 1022. 宠物小精灵之收服(多维度01背包)

在这里插入图片描述

两个维度的权值,直接多加一维数组,多加一维循环即可。但是要注意因为多维度所以没办法直接输出答案,所以应该从大到小查找最优的答案,一旦被更新过就说明是待选答案

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>

using namespace std;

const int N = 20007, M = 507, K = 107, INF = 0x3f3f3f3f;

int n, m, kk;
int f[M][K];

//f[i][j] : 表示收集了 j 个精灵 , 体力为 i , 用的最小的精灵球数量

int main()
{   memset(f, 0x3f, sizeof f);
    f[0][0] = 0;
    scanf("%d%d%d", &n, &m, &kk);
    for(int i = 1, c, d; i <= kk; ++ i){
        scanf("%d%d", &c, &d);
        for(int j = m; j >= d; -- j){//体力
            for(int k = kk; k >= 1; -- k)//精灵数
            if(f[j - d][k - 1] + c <= n)//如果可以收得下
                f[j][k] = min(f[j][k], f[j - d][k - 1] + c);//最少精灵球
        }
    }
    
    for(int k = kk; k >= 0; -- k){
        int ans = INF;
        for(int j = 0; j < m; ++ j){
            if(f[j][k] != INF && j < ans){
                ans = j;
            }
        }
        if(ans != INF){
            printf("%d %d\n", k, m - ans);
            return 0;
        }
    }
    return 0;
}

E、AcWing 426. 开心的金明

在这里插入图片描述

//二维变量
//质量和价值,价值根据题意为质量*价格


#include<cstdio>
#include<algorithm>

using namespace std;

const int N = 50007;

int f[N];
int n, m;
int v[N], w[N];

int main(){
    scanf("%d%d", &m, &n);
    for(int i = 1; i <= n; ++ i){
        scanf("%d%d", &v[i], &w[i]);
        w[i] *= v[i];
    }
    
    for(int i = 1; i <= n; ++ i){
        for(int j = m; j >= v[i]; -- j){
            f[j] = max(f[j], f[j - v[i]] + w[i]);
        }
    }
    printf("%d\n", f[m]);
    return 0;
}

二、完全背包

A、AcWing 1023. 买书(完全背包模板求方案数)

在这里插入图片描述

#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

int a[] = {10, 20, 50, 100};
int n;
int f[1007];

int main(){
    cin >> n;
    f[0] = 1;
    for(int i = 0; i < 4; ++ i){
        for(int j = a[i]; j <= n; ++ j)
        f[j] += f[j - a[i]];
    }
    cout << f[n] << endl;
    return 0;
}

B、AcWing 1021. 货币系统(模板)

在这里插入图片描述

#include<iostream>
#include<algorithm>

using namespace std;
typedef long long ll;
const int N = 5007;

ll f[N];
int n, m;
int a[N];

int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; ++ i)
        cin >> a[i];
    f[0] = 1;
    for(int i = 1; i <= n; ++ i){
        for(int j = a[i]; j <= m; ++ j){
            f[j] += f[j - a[i]]; 
        }
    }
    cout << f[m] << endl;
    return 0;
}

C、AcWing 532. 货币系统(思维)

在这里插入图片描述

在这里插入代码片

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

const int N = 250007;
int n, m;
bool f[N];
int a[N];

int main(){
    int t;
    scanf("%d", &t);
    while(t -- ){
        scanf("%d", &n);
        int ans = 0;
        for(int i = 1; i <= n; ++ i)
            scanf("%d", &a[i]);
        sort(a + 1, a + 1 + n);//要排序,因为最小的肯定要留下来,因为只有小的才能凑出大的
        
        int x = a[n];
        memset(f, 0, sizeof f);
        
        f[0] = true;//!
        
        for(int i = 1; i <= n; ++ i){
            if(!f[a[i]])ans ++ ;
            for(int j = a[i]; j <= x; ++ j){
                f[j] |= f[j - a[i]];
            }
        }
        cout << ans << endl;
    }
    return 0;
}

三、多重背包

A、AcWing 4. 多重背包问题 I(模板)

在这里插入图片描述

#include<iostream>
#include<algorithm>
#include<cstdio>

using namespace std;

const int N = 100007;

int w[N], v[N];
int n, val;
int f[N];

int main(){
    scanf("%d%d", &n, &val);
    
    int cnt = 0;
    for(int i = 1, a, b, c; i <= n; ++ i){
        scanf("%d%d%d", &a, & b, &c);
        
        for(int j = 1; j <= c; j <<= 1){
            w[++ cnt ] = a * j;
            v[cnt] = b * j;
            c -= j;
        }
        if(c)w[++ cnt ] = a * c, v[cnt] = b * c; 
    }
    
    for(int i = 1; i <= cnt; ++ i){
        for(int j = val; j >= w[i]; -- j){
            f[j] = max(f[j], f[j - w[i]] + v[i]);
        }
    }
    cout << f[val] << endl;
    return 0;
}

B、AcWing 6. 多重背包问题 III

四、混合背包

AcWing 7. 混合背包问题(由多重背包修改得到)

在这里插入图片描述
直接按照多重背包做就好,把完全背包的无限量换成一个挺大的数就行了。

#include<iostream>
#include<algorithm>
#include<cstdio>

using namespace std;

const int N = 100007;

int w[N], v[N];
int n, val;
int f[N];

int main(){
    scanf("%d%d", &n, &val);
    
    int cnt = 0;
    for(int i = 1, a, b, c; i <= n; ++ i){
        scanf("%d%d%d", &a, & b, &c);
        if(c == -1)c = 1;
        if(c == 0)c = 99999;
        for(int j = 1; j <= c; j <<= 1){
            w[++ cnt ] = a * j;
            v[cnt] = b * j;
            c -= j;
        }
        if(c)w[++ cnt ] = a * c, v[cnt] = b * c; 
    }
    
    for(int i = 1; i <= cnt; ++ i){
        for(int j = val; j >= w[i]; -- j){
            f[j] = max(f[j], f[j - w[i]] + v[i]);
        }
    }
    cout << f[val] << endl;
    return 0;
}

五、二维费用的背包问题

A、AcWing 8. 二维费用的背包问题

在这里插入图片描述

#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>

using namespace std;

const int N = 20005;

int n, V, M;
int f[N][N];
int v[N], m[N], w[N];

int main(){
    cin >> n >> V >> M ;
    for(int i = 1; i <= n; ++ i)
        scanf("%d%d%d", &v[i], &m[i], &w[i]);
    for(int i = 1; i <= n; ++ i){
        for(int j = V; j >= v[i]; -- j)
            for(int k = M; k >= m[i]; -- k){
                f[j][k] = max(f[j][k], f[j - v[i]][k - m[i]] + w[i]);
            } 
    }
    cout << f[V][M] << endl;
    return 0;
}

B、AcWing 1020. 潜水员

在这里插入图片描述

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>

/*f[i][j]表示氧气至少为i,氮气至少为j的最小重量*/

using namespace std;
const int N = 1007;

int n, m, k;
int a[N], b[N], w[N];
int f[N][N];

int main(){
    scanf("%d%d%d", &n, &m, &k);
    for(int i = 1; i <= k; ++ i)
        scanf("%d%d%d", &a[i], &b[i], &w[i]);
    memset(f, 0x3f, sizeof f);
    f[0][0] = 0;
    
    for(int i = 1; i <= k; ++ i)
        for(int j = n; j >= 0; -- j)
            for(int l = m; l >= 0; -- l){
                int a1 = a[i] + j;
                if(a1 > n)a1 = n;
                int a2 = b[i] + l;
                if(a2 > m)a2 = m;
                f[a1][a2] = min(f[a1][a2], f[j][l] + w[i]);
            }
    cout << f[n][m] << endl;
    return 0;
}


六、分组背包

A、AcWing 1013. 机器分配(DP后查找最后的方案、倒着再来一遍)

在这里插入图片描述

在这里插入图片描述

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;

const int N = 5007;

int n, m;
int f[N][N];
int w[N][N];
int way[N];

int main(){
    cin >> n >> m;
    
    for(int i = 1; i <= n; ++ i)
    for(int j = 1; j <= m; ++ j)
    scanf("%d", &w[i][j]);
    
    for(int i = 1; i <= n; ++ i){
        for(int j = 0; j <= m; ++ j){
            for(int k = 0; k <= j; ++ k)
            f[i][j] = max(f[i][j], f[i - 1][j - k] + w[i][k]);
        }
    }
    cout << f[n][m] << endl;
    
    int j = m;
    for(int i = n; i; -- i){//倒着再走一次,找到方案,因为DP的时候是没办法存方案的,DP的时候经常反悔,当前最优不一定是全局最优
        for(int k = 0; k <= j; ++ k){
            if(f[i][j] == f[i - 1][j - k] + w[i][k]){
                way[i] = k;
                j -= k;
                break;
            }
        }
    }
    
    for(int i = 1; i <= n; ++ i){
        printf("%d %d\n", i, way[i]);
    }
    return 0;
}

七、背包问题求方案数

A、AcWing 11. 背包问题求方案数

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 5007, INF = 0x3f3f3f3f, mod = 1e9 + 7;

int n, m;
int a[N];
int f[N];
int v[N], w[N];
int cnt[N];
int main(){
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; ++ i)
    scanf("%d%d", &v[i], &w[i]);
    for(int i = 0; i <= m; ++ i)cnt[i] = 1;
    for(int i = 1; i <= n; ++ i){
        for(int j = m; j >= v[i]; -- j){
            int val = f[j - v[i]] + w[i];
            if(val > f[j]){
                f[j] = val;
                cnt[j] = cnt[j - v[i]];
            }
            else if(val == f[j]){
                cnt[j] = (cnt[j] + cnt[j - v[i]] * 1ll) % mod;
            }
        }
    }
    printf("%d", cnt[m]);
    return 0;
}

AcWing 12. 背包问题求具体方案

在这里插入图片描述

在这里插入图片描述

#include<cstdio>
#include<algorithm>
#include<iostream>
#include<cstring>

using namespace std;

const int N = 5007;
int n, m;
int f[N][N];
int v[N], w[N];
int main(){
    scanf("%d%d", &n, &m);
    
    for(int i = 1; i <= n; ++ i)
        scanf("%d%d", &v[i], &w[i]);
    for(int i = n; i >= 1; -- i){
        for(int j = m; j >= 0; -- j){
            f[i][j] = f[i + 1][j];
            if(j >= v[i])
                f[i][j] = max(f[i][j], f[i + 1][j - v[i]] + w[i]);
        }
    }
    int j = m;
    for(int i = 1; i <= n; ++ i){
        if(j >= v[i] && f[i][j] == f[i + 1][j - v[i]] + w[i]){
            printf("%d ", i);
            j -= v[i];
        }
        
    }
    puts("");
    return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_45697774/article/details/108193548