01背包问题(简单)总结

写在前面:

这些背包问题都是01背包或其衍生体。难度为易,专门为初学背包问题提供的。

前置知识:背包九讲中的01背包问题

题单链接:背包问题(简单)

1、P1048 采药

1、P1048 采药
01背包模板题

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

const int N = 110,M = 1010;

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

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

空间优化到O(M)后:

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

const int N = 110,M = 1010;

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

int main(){
    cin >> m >> n;
    for(int i = 1; i <= n; i ++ ){
        cin >> v[i] >> w[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]);
        }
    }
    cout<<f[m]<<endl;
    return 0;
}

2、P1164 小A点菜

2、P1164 小A点菜
01背包求方案数模板题

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

const int N = 110,M = 1e4 + 10;

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

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

3、P1049 装箱问题

3、P1049 装箱问题

  • 空间即为体积也为价值。为了使得剩余空间最小,需要箱子内的物品体积越大。
#include<cstring>
#include<cstdio>
#include<iostream>
 
using namespace std;

const int N = 40,M = 2e4 + 10;

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

int main(){
    cin >> m >> n;
    for(int i = 1; i <= n; i ++ ) cin >> 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]] + v[i]);
        }
    }
    cout<<m-f[m]<<endl;
    return 0;
}

4、P1060 开心的金明

4、P1060 开心的金明
价值为v[i]*w[i]

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

using namespace std;

#define x first
#define y second
#define mm(a,x) memset(a,x,sizeof a)
#define endl "\n"

const int N = 3e4 + 10;

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

int main() {
	cin >> m >> n;
	for(int i = 0; i < n; i ++ ){
		cin >> w[i] >> v[i];
		v[i] *= w[i];
	}
	for(int i = 0; i < n; i ++ ){
		for(int j = m; j >= w[i]; j --){
			f[j] = max(f[j],f[j - w[i]] + v[i]);
		}
	}
	cout<<f[m];		
	return 0;
}

5、P1510 精卫填海

5、P1510 精卫填海

  • 填平之后剩下最大体力转换为求消耗最小体力且能填平
  • 从小开始枚举消耗体力值直到不小于填平体积
#include<cstring>
#include<cstdio>
#include<iostream>
 
using namespace std;

const int N = 1e4 + 10,M = 1e4 + 10;

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

int main(){
    cin >> maxv >> n >> m;
    for(int i = 1; i <= n; i ++ ){
        cin >> 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]);
        }
    }
    if(f[m] < maxv){
        puts("Impossible");
    }else{
        int k = 0;
        while(f[k] < maxv) k ++;
        printf("%d",m - k);
    }
    return 0;
}

6、P1926 小书童——刷题大军

6、P1926 小书童——刷题大军

数据范围较小,可以暴搜

方法一:暴搜

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

using namespace std;

const int N = 200;

int n,m,k,r;
int s[N],v[N],w[N];
int maxv;

void dfs(int u,int a,int b){ //a:时间,b:分数
    if(maxv >= r - a) return ;
    if(b >= k) maxv = r - a;
    if(u >= m) return ;
    for(int i = u + 1; i <= m; i ++ ){
        dfs(i,a + v[i],b + w[i]);
    }
}

int main(){
    cin >> n >> m >> k >> r;
    for(int i = 1; i <= n; i ++ ) cin >> s[i];
    for(int i = 1; i <= m; i ++ ) cin >> v[i];
    for(int i = 1; i <= m; i ++ ) cin >> w[i];
    dfs(0,0,0);
    sort(s + 1,s + n + 1);
    int ans = 0;
    for(int i = 1; i <= n; i ++ ){
        if(maxv >= s[i]) maxv -= s[i],ans += 1;
    }
    cout<<ans;
    return 0;
}

方法二:01背包

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

using namespace std;

const int N = 200;

int n,m,k,r;
int s[N],v[N],w[N];
int f[N];

int main(){
    cin >> n >> m >> k >> r;
    for(int i = 1; i <= n; i ++ ) cin >> s[i];
    sort(s,s+n);
    for(int i = 1; i <= n; i ++ ) s[i] += s[i - 1];
    for(int i = 1; i <= m; i ++ ) cin >> v[i];
    for(int i = 1; i <= m; i ++ ) cin >> w[i];
    for(int i = 1; i <= m; i ++ ){
        for(int j = r; j >= v[i]; j --){
            f[j] = max(f[j],f[j - v[i]] + w[i]);
        }
    }
    int c = 0;
    while(f[c] < k) c ++;
    int ans = r - c;
    int i = n;
    while(ans < s[i]) i--;
    cout<<i;
    return 0;
}

7、P1802 5倍经验日

7、P1802 5倍经验日

  • 这道题细节是输了也有经验吃。
  • 输了就是j<user[i],把这些都加上lose[i]
  • 其他都可以输也可以赢,0也要带上
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

typedef long long LL;

const int N = 1e3 + 10;

int n,m;
LL lose[N],win[N];
int v[N];
LL f[N];

int main(){
    cin >> n >> m;
    for(int i = 1; i <= n; i ++ ) cin >> lose[i] >> win[i] >> v[i];
    for(int i = 1; i <= n; i ++ ){
        for(int j = m; j >= v[i]; j --){
            f[j] = max(f[j] + lose[i],f[j - v[i]] + win[i]);
        }
        for(int j = v[i] - 1; j >= 0; j --){
            f[j] += lose[i];
        }
    }
    cout<<f[m]*5;
    return 0;
}

8、P1734 最大约数和

8、P1734 最大约数和

  • 先预处理出1~S每个数的约数和
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

typedef long long LL;

const int N = 1e3 + 10;

int n;
int v[N];
int f[N];

int calc(int n){
    int s = 0;
    for(int i = 1; i <= n/i; i ++ ){
        if(n % i == 0){
            s += i;
            if(i != n/i) s += n/i;
        }
    }
    s-=n;
    return s;
}

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

9、P2392 kkksc03考前临时抱佛脚

9、P2392 kkksc03考前临时抱佛脚

数据较小,可以暴搜

方法一:暴搜

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

using namespace std;

typedef long long LL;

const int N = 30;

int s1[N],s[N][N];
int minv;
int ans;

void dfs(int u,int k,int l,int r){
    if(k == s1[u] + 1){
        minv = min(minv,max(l,r));
        return ;
    }
    l += s[u][k];
    dfs(u,k + 1,l,r);
    l -= s[u][k];
    r += s[u][k];
    dfs(u,k + 1,l,r);
    r -= s[u][k];
}

int main(){
    for(int i = 1; i <= 4; i ++ ) cin >> s1[i];
    for(int i = 1; i <= 4; i ++ ){
        for(int j = 1; j <= s1[i]; j ++ ){
            cin >> s[i][j];
        }
    }
    for(int i = 1; i <= 4; i ++){
        minv = 1e9;
        dfs(i,1,0,0);
        ans += minv;
    }
    cout<<ans;
    return 0;
}

方法二:01背包

  • 找出每道题左脑能最接近一半时间,用总时间减去它得到最优时间(即max(t1,t2))
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

typedef long long LL;

const int N = 1e4 + 10;

int s1[N],s[N][N];
int ans;
int f[N],sum[N];

int main(){
    for(int i = 1; i <= 4; i ++ ) cin >> s1[i];
    for(int i = 1; i <= 4; i ++ ){
        for(int j = 1; j <= s1[i]; j ++ ){
            cin >> s[i][j],sum[i] += s[i][j];
        }
    }
    for(int i = 1; i <= 4; i ++){
        memset(f,0,sizeof f);
        for(int j = 1; j <= s1[i]; j ++ ){
            for(int k = sum[i]/2; k >= s[i][j]; k --) f[k] = max(f[k],f[k - s[i][j]] + s[i][j]);
        }
        ans += sum[i] - f[sum[i]/2];
    }
    cout<<ans;
    return 0;
}

10、P1466 [USACO2.2]集合 Subset Sums

10、P1466 [USACO2.2]集合 Subset Sums

  • 如果和为奇数,不可能划分
  • 求出能达到一半集合总和的总方案数,最后除2去重

暴搜骗分

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

using namespace std;

typedef long long LL;
const int N = 50;

int n;
int ans;
int s;

int dfs(int u,int l,int r){
    if(l > s/2 || r > s/2) return 0;
    if(u == 0){
        if(l == r) return 1;
        return 0;
    }
    return dfs(u - 1,l + u,r) + dfs(u - 1,l,r + u);
}

int main(){
    ios::sync_with_stdio(false);
    cin >> n;
    s = (n*(n+1))/2;
    if(s % 2 == 1) cout<<0;
    else{
        ans = dfs(n,0,0);
        cout<<ans/2;
    }
    return 0;
}

01背包

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

using namespace std;

typedef long long LL;
const int N = 3e3 + 10;

int n;
LL f[N];
int s;

int main(){
    ios::sync_with_stdio(false);
    cin >> n;
    s = (n*(n+1))/2;
    if(s % 2 == 1){
        cout<<0;
    }else{
        f[0] = 1;
        for(int i = 1; i <= n; i ++ ){
            for(int j = s/2; j >= i; j --){
                f[j] += f[j - i];
            }
        }
        cout<<f[s/2]/2;
    }
    return 0;
}

11、P2370 yyy2015c01 的 U 盘

11、P2370 yyy2015c01 的 U 盘

  • 找出最小使用的接口,这就需要从所有可能的接口大小中找,枚举效率低,这里采用二分答案
  • 剩下的就交给背包
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

typedef long long LL;
const int N = 1e3 + 10;

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

bool check(int x){
    memset(f,0,sizeof f);
    for(int i = 1; i <= n; i ++ ){
        if(v[i]<=x)
        for(int j = m; j >= v[i]; j --){
            f[j] = max(f[j],f[j - v[i]] + w[i]);
        }
    }
    return f[m] >= p;    
}

int main(){
    ios::sync_with_stdio(false);
    cin >> n >> p >> m;
    int l = 0,r = 0;
    for(int i = 1; i <= n; i ++ ){
        cin >> v[i] >> w[i];
        r = max(r,v[i]);
    }
    bool flag = true;
    while(l < r){
        int mid = l + r >> 1;
        if(check(mid)) r = mid,flag = false;
        else l = mid + 1;
    }
    if(flag) puts("No Solution!");
    else cout<<l<<endl;
    return 0;
}

12、CF294B Shaass and Bookshelf

12、CF294B Shaass and Bookshelf

这道题就特又意思,其中这句很关键

The sum of the widths of the horizontal books must be no more than the total thickness of the vertical books.
水平放置的书的宽度和不能多于竖直放置的书的总厚度。

  • 分两类,一类取上面,一类在下面。
  • 题目要让求出最小的长度:因此,要让下面那类最小,即下面的书组合成的厚度和最小。
  • 厚度总和是一定的,那就让上面的厚度和最大,并且上面的宽度一定要是小的
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

typedef long long LL;
const int N = 1e3 + 10;

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

int main(){
    ios::sync_with_stdio(false);
    cin >> n;
    for(int i = 1; i <= n; i ++){
        cin >> t[i] >> w[i];
        m += t[i];
    }
    memset(f,0x3f,sizeof f);
    f[0] = 0;
    for(int i = 1; i <= n; i ++ ){
        for(int j = m; j >= t[i]; j --){
            f[j] = min(f[j],f[j - t[i]] + w[i]);
        }
    }
    int k = m;
    while(k > 0 && f[k] > m - k) k --;
    cout<<m-k;
    return 0;
}

13、CF19B Checkout Assistant

13、CF19B Checkout Assistant

  • 把某商品从推车上偷走需要1秒,不偷走需要t[i] + 1秒(额外1秒用来偷其他商品)
  • 要找到最小金额,因此初始化f[0] = 0,其他为极大
  • 花费时间至少为n秒,在所有可能花费中找到最小金额。
#include<cstring>
#include<cstdio>
#include<iostream>
#include<algorithm>

using namespace std;

typedef long long LL;
const int N = 1e4 + 10;

LL n,m;
LL t[N],c[N];
LL f[N];

int main(){
    ios::sync_with_stdio(false);
    cin >> n;
    for(int i = 1; i <= n; i ++ ){
        cin >> t[i] >> c[i];
        t[i] += 1;
        m = max(m,t[i]);
    }
    m += n;
    memset(f,0x3f,sizeof f);
    f[0] = 0;
    for(int i = 1; i <= n; i ++ ){
        for(int j = m; j >= t[i]; j --){
            f[j] = min(f[j],f[j - t[i]] + c[i]);
        }
    }
    LL ans = 1e18;
    for(int i = n; i <= m; i ++ ) ans = min(ans,f[i]);
    cout<<ans;
    return 0;
}

14、P4141 消失之物

14、P4141 消失之物

f[i][0]:不丢掉i的所有方案数
f[i][1]:丢掉i的所有方案数

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

using namespace std;

#define x first
#define y second
#define mm(a,x) memset(a,x,sizeof a)
#define endl "\n"
#define inf 0x3f3f3f3f

typedef long long ll;

const int N = 1e4 + 10;

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

int main() {
    cin >> n >> m;
    for(int i = 1; i <= n; i ++ ) cin >> v[i];
    f[0][0] = f[0][1] = 1;
    for(int i = 1; i <= n; i ++ ){
        for(int j = m; j >= v[i]; j --){
            f[j][0] += f[j - v[i]][0];
            f[j][0] %= 10;
        }
    }
    for(int i = 1; i <= n; i ++ ){
        for(int j = 1; j <= m; j ++ ){
            if(j >= v[i]) f[j][1] = (f[j][0] - f[j - v[i]][1] + 10) % 10;
            else f[j][1] = f[j][0]%10;
            cout<<f[j][1];
        }
        puts("");
    }
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值