【总结】01背包问题

A.Subset Sums集合

分析:设dp[i][j]表示前i个数和为j的方案数,则:
dp[i][j]=dp[i-1][j]+dp[i-1][j-i] (j>=i,dp[n][0]=1)
下面给出两种实现方法(推荐第一种):

#include<cstdio> #include<algorithm> #define ll long long using namespace std; const int maxn=5005; ll n,m,dp[maxn]; int main() { scanf("%lld",&n); m=n*(n+1)/2; if(m&1) { printf("0"); return 0; } m/=2; dp[0]=1; for(int i=1;i<=n;i++) { for(int j=m;j>=i;j--) { dp[j]+=dp[j-i]; } } printf("%lld",dp[m]/2); }

玄学做法:

#include<cstdio> #include<algorithm> using namespace std; const int maxn=5005; int n,m,dp[40][maxn]; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) m+=i; if(m&1) { printf("0"); return 0; } m/=2; dp[0][0]=1; for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(j<i) dp[i][j]=dp[i-1][j]; else dp[i][j]=dp[i-1][j]+dp[i-1][j-i]; } } printf("%d",dp[n][m]); }

B.黄金树

分析:
// 设dp[i][j]表示前i天砍前j个树的最大利润
//若砍的树一定,那么一定是增长率大的最后砍
//所以我们选择排序(为了让方案变得有序)
//若砍j,则dp[i][j]=dp[i-1][j-1]+w[j]+(i-1)*k[j]
//若不砍j,则dp[i][j]=dp[i][j-1]
//(j是前i个中k[j]最大的,若不在第j天砍,则算出来一定不是最优,即该方案小于上方案)
//本题想到了不砍j时有可能在前面砍,这样就不能用dp[i][j-1]表示
//而排序则避免了这种决策(未想到)

#include<cstdio> #include<algorithm> #include<cstring> #define ll long long using namespace std; const int maxn=255; int n,m,dp[maxn][maxn]; struct node{ int w,k; }a[maxn]; bool cmp(node x,node y) { return x.k<y.k; } int main() { int t; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&m); memset(dp,0,sizeof(dp)); for(int i=1;i<=n;i++) scanf("%d",&a[i].w); for(int i=1;i<=n;i++) scanf("%d",&a[i].k); sort(a+1,a+1+n,cmp); for(int i=1;i<=min(m,n);i++) { for(int j=i;j<=n;j++) { dp[i][j]=max(dp[i-1][j-1]+a[j].w+(i-1)*a[j].k,dp[i][j-1]); } } printf("%d\n",dp[min(m,n)][n]); } }

C.差最小

分析:和背包问题没有什么关系。
dp[i][j]表示以第i个数为结尾,取出序列长度为j的方案数
答案取max。

#include<cstdio> #include<algorithm> #include<cmath> #include<cstring> using namespace std; const int maxn=305; int n,c,ans,a[maxn],dp[maxn][maxn]; int main() { memset(dp,0x3f3f3f3f,sizeof(dp)); ans=0x3f3f3f3f; scanf("%d%d",&n,&c); for(int i=1;i<=n;i++) { scanf("%d",&a[i]); } for(int i=1;i<=n;i++) { for(int j=1;j<=c&&j<=i;j++) { if(j==1) dp[i][j]=0; else { for(int k=1;k<i;k++) { dp[i][j]=min(dp[i][j],dp[k][j-1]+abs(a[i]-a[k])); } } if(j==c) ans=min(ans,dp[i][j]); } } printf("%d",ans); }

D.夏季特惠【第五周】

分析:
这是一道典型的01背包的变形,总容量为x,关键在于如何求出每个物品所占用的空间(价值已确定)
设每个物品花费bi,优惠ci,则sum ci>=sum bi-x
变形得sum(bi-ci)<=x
bi-ci=bi-(ai-bi)=2*bi-ci, 这就是每个物品占用的空间,其价值为wi,背包容量为x

注意:2*bi-ci可能为负,我们可以提前把它装进背包里(背包总容量增加)

#include<cstdio> #include<algorithm> #include<cstring> #define ll long long using namespace std; const int maxn=1e6+5,size=505; ll n,x,dp[maxn],ans,cnt; struct node{ ll v,w; }a[size]; int main() { scanf("%lld%lld",&n,&x); for(int i=1;i<=n;i++) { ll ai,bi,vi,wi; scanf("%lld%lld%lld",&ai,&bi,&wi); vi=bi*2-ai; if(vi<=0) { x-=vi; ans+=wi; } else { a[++cnt].v=vi; a[cnt].w=wi; } } for(int i=1;i<=cnt;i++) { for(int j=x;j>=a[i].v;j--) { dp[j]=max(dp[j],dp[j-a[i].v]+a[i].w); } } printf("%lld",dp[x]+ans); }

__EOF__

本文作者仰望星空的蚂蚁
本文链接https://www.cnblogs.com/cqbzly/p/17530428.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   仰望星空的蚂蚁  阅读(12)  评论(0编辑  收藏  举报  
相关博文:
阅读排行:
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 记一次.NET内存居高不下排查解决与启示
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· DeepSeek 开源周回顾「GitHub 热点速览」
点击右上角即可分享
微信分享提示