01背包
2016-07-18 20:53
447 查看
状态转移方程:dp[i][j] = max(dp[i-1][j], dp[i-1][j-c[i]] + w[i]) (正背)
可简化为一维方程:dp[i] = max(dp[i], dp[i-c[i]] + w[i]) (倒背)
专题链接:01背包
密码:acmaker
很裸的背包,大意是把骨头放入包中,给出包的大小、骨头数量、没块骨头体积及价值,求能装下的骨头的最大价值。给出2种解法,也是01背包基本模板
这题是珠宝,给出珠宝数量,重量上限,每个珠宝的重量和价值,求不超过上限的情况下珠宝的最高价值,也是裸背包。
饭卡余额大于等于5元可以任意购买(即使结账后余额为负),小于5元时不能购买(即使余额足够)。
给出的只有菜品的种类和单价,以及卡的余额。每种菜只能买一次。
这里菜的单价既是代价c,也是价值w,卡的余额有5元的限制可以先将背包容量减5。将菜品按价格排序,最贵的菜先不计算,最后在余额最少的情况下再减去最贵的菜即可。当然要保证此时的余额大于等于5元。
给出磁道长度,歌曲数量及每首歌的长度,求最大歌曲播放时间并给出播放的歌曲列表。
这里把歌曲时间长度作为代价,也作为价值,难点在于歌曲列表的记录。用vis数组来记录歌曲是否被添加,一旦dp[j]<=dp[j-c[i]]+c[i],就说明dp[i]的值要被改变,此时将vis[i][j]标记为1,在输出时对vis进行遍历即可。
给出硬币的数量和价值,2人分硬币,求2人所分价值的最小值。
将硬币的价值同时看做代价和价值,背包容量设为硬币总价值的1/2,这样分出来的价值最大值就是最接近总价值1/2的小值,而另一人是大值,二人的价值差就等于总价值-背包最大值*2。
给出被捕的概率的安全上限,银行数,每家银行可获得的价值和被捕概率,求安全的情况下最多可以偷多少钱。
概率因为是小数,无法作为下标,所以用银行价值作为代价,概率作为价值,求在偷得某个价值的情况下的最大逃生率,再与安全逃生率比较,只要超过安全逃生线即可。
这里的概率要用乘法,因为逃生一次接一次,要在之前的逃生率上再重新计算逃生率。
给出牛的数量和每头牛的智商及幽默感,选出若干头牛使智商总和及幽默感总和为正的情况下智商+幽默感总和的最大值。
难点在于如何保证智商和幽默感同时取到最大。奇淫奇巧:正背包+逆背包
可简化为一维方程:dp[i] = max(dp[i], dp[i-c[i]] + w[i]) (倒背)
专题链接:01背包
密码:acmaker
A - Bone Collector
HDU 2602很裸的背包,大意是把骨头放入包中,给出包的大小、骨头数量、没块骨头体积及价值,求能装下的骨头的最大价值。给出2种解法,也是01背包基本模板
//二维解法 #include"cstdio" #include"iostream" #include"algorithm" #include"cstring" #include"string" #include"cmath" int c[1005],w[1005]; //c为每件物品代价,w为每件物品价值 int dp[1005][1005]; int max(int a,int b) { return a>b?a:b; } int main() { int n; scanf("%d",&n); while(n--) { int N,V; scanf("%d%d",&N,&V); memset(c,0,sizeof c); memset(w,0,sizeof w); memset(dp,0,sizeof dp); int i; for(i=1;i<=N;i++) scanf("%d",&w[i]); for(i=1;i<=N;i++) scanf("%d",&c[i]); for(i=1;i<=N;i++) for(int j=0;j<=V;j++) //正背包 if(c[i]<=j) //判断当前物品是否可以入包 dp[i][j]=max(dp[i-1][j],dp[i-1][j-c[i]]+w[i]); //真·状态转移方程 else dp[i][j]=dp[i-1][j]; int ans=0; for(i=0;i<=V;i++) //检索最大值 { if(dp [i]>ans)ans=dp [i]; } printf("%d\n",ans); } return 0; }
//一维解法 #include"cstdio" #include"iostream" #include"algorithm" #include"cstring" #include"string" #include"cmath" int c[1005],w[1005]; //c为每件物品代价,w为每件物品价值 int dp[1005]; int max(int a,int b) { return a>b?a:b; } int main() { int n; scanf("%d",&n); while(n--) { int N,V; scanf("%d%d",&N,&V); memset(c,0,sizeof c); memset(w,0,sizeof w); memset(dp,0,sizeof dp); int i; for(i=1;i<=N;i++) scanf("%d",&w[i]); for(i=1;i<=N;i++) scanf("%d",&c[i]); for(i=1;i<=N;i++) for(int j=V;j>=c[i];j--) //倒背包 dp[j]=max(dp[j],dp[j-c[i]]+w[i]); //状态转移方程 printf("%d\n",dp[V]); } return 0; }
B - Charm Bracelet
POJ 3624这题是珠宝,给出珠宝数量,重量上限,每个珠宝的重量和价值,求不超过上限的情况下珠宝的最高价值,也是裸背包。
#include"cstdio" #include"iostream" #include"algorithm" #include"cstring" #include"string" #include"cmath" int c[3405],w[3405]; //c为每件物品代价,w为每件物品价值 int dp[12890]; int max(int a,int b) { return a>b?a:b; } int main() { int N,V; scanf("%d%d",&N,&V); memset(c,0,sizeof c); memset(w,0,sizeof w); memset(dp,0,sizeof dp); int i; for(i=1;i<=N;i++) scanf("%d%d",&c[i],&w[i]); for(i=1;i<=N;i++) for(int j=V;j>=c[i];j--) //倒背包 dp[j]=max(dp[j],dp[j-c[i]]+w[i]); //状态转移方程 printf("%d\n",dp[V]); return 0; }
C - 饭卡
HDU 2546饭卡余额大于等于5元可以任意购买(即使结账后余额为负),小于5元时不能购买(即使余额足够)。
给出的只有菜品的种类和单价,以及卡的余额。每种菜只能买一次。
这里菜的单价既是代价c,也是价值w,卡的余额有5元的限制可以先将背包容量减5。将菜品按价格排序,最贵的菜先不计算,最后在余额最少的情况下再减去最贵的菜即可。当然要保证此时的余额大于等于5元。
#include"cstdio" #include"iostream" #include"algorithm" #include"cstring" #include"string" #include"cmath" using namespace std; int c[1005]; //c为每件物品代价,同时为物品价值 int dp[1005]; int max(int a,int b) { return a>b?a:b; } int main() { int N; while(~scanf("%d",&N),N) { memset(c,0,sizeof c); memset(dp,0,sizeof dp); int i; for(i=1;i<=N;i++) scanf("%d",&c[i]); int V; scanf("%d",&V); if(V<5) //余额不足 { printf("%d\n",V); continue; } V=V-5; sort(c+1,c+N+1); for(i=1;i<=N-1;i++) for(int j=V;j>=c[i];j--) //倒背包 dp[j]=max(dp[j],dp[j-c[i]]+c[i]); //状态转移方程 int ans=V+5-dp[V]-c ; printf("%d\n",ans); } return 0; }
D - CD
UVA 624给出磁道长度,歌曲数量及每首歌的长度,求最大歌曲播放时间并给出播放的歌曲列表。
这里把歌曲时间长度作为代价,也作为价值,难点在于歌曲列表的记录。用vis数组来记录歌曲是否被添加,一旦dp[j]<=dp[j-c[i]]+c[i],就说明dp[i]的值要被改变,此时将vis[i][j]标记为1,在输出时对vis进行遍历即可。
#include"cstdio" #include"iostream" #include"algorithm" #include"cstring" #include"string" #include"cmath" using namespace std; int c[25]; //c为每件物品代价,w为每件物品价值 int dp[10005]; int vis[25][10005]; //记录该歌曲是否被添加 int max(int a,int b) { return a>b?a:b; } int main() { int N,V; while(~scanf("%d",&V)) { memset(c,0,sizeof c); memset(dp,0,sizeof dp); memset(vis,0,sizeof vis); int i; scanf("%d",&N); for(i=1;i<=N;i++) scanf("%d",&c[i]); for(i=1;i<=N;i++) for(int j=V;j>=c[i];j--) //倒背包 { if(dp[j]<=dp[j-c[i]]+c[i]) { dp[j]=dp[j-c[i]]+c[i]; //状态转移方程 vis[i][j]=1; //访问标记 } } int j; for(j=V,i=N;i>0;i--) //共N首歌曲 { if(vis[i][j]) { printf("%d ",c[i]); j-=c[i]; //寻找前一个歌曲 } } printf("sum:%d\n",dp[V]); } return 0; }
E - Dividing coins
UVA 562给出硬币的数量和价值,2人分硬币,求2人所分价值的最小值。
将硬币的价值同时看做代价和价值,背包容量设为硬币总价值的1/2,这样分出来的价值最大值就是最接近总价值1/2的小值,而另一人是大值,二人的价值差就等于总价值-背包最大值*2。
#include"cstdio" #include"iostream" #include"algorithm" #include"cstring" #include"string" #include"cmath" int c[10000]; //c为每件物品代价,w为每件物品价值 int dp[50005]; int max(int a,int b) { return a>b?a:b; } int main() { int n; scanf("%d",&n); while(n--) { int N; scanf("%d",&N); int V=0; memset(c,0,sizeof c); memset(dp,0,sizeof dp); int i; for(i=1;i<=N;i++) { scanf("%d",&c[i]); V+=c[i]; //总价值 } for(i=1;i<=N;i++) for(int j=V/2;j>=c[i];j--) //倒背包 dp[j]=max(dp[j],dp[j-c[i]]+c[i]); //状态转移方程 printf("%d\n",V-dp[V/2]*2); } return 0; }
F - Robberies
HDU 2955给出被捕的概率的安全上限,银行数,每家银行可获得的价值和被捕概率,求安全的情况下最多可以偷多少钱。
概率因为是小数,无法作为下标,所以用银行价值作为代价,概率作为价值,求在偷得某个价值的情况下的最大逃生率,再与安全逃生率比较,只要超过安全逃生线即可。
这里的概率要用乘法,因为逃生一次接一次,要在之前的逃生率上再重新计算逃生率。
#include"cstdio" #include"iostream" #include"algorithm" #include"cstring" #include"string" #include"cmath" using namespace std; int c[105]; double w[105]; double dp[10005]; double max(double a,double b) { return a>b?a:b; } int main() { int t; scanf("%d",&t); int N,V; double P; while(t--) { scanf("%lf %d",&P,&N); P=1-P; //逃跑概率 memset(c,0,sizeof c); memset(w,0,sizeof w); memset(dp,0,sizeof dp); dp[0]=1; V=0; int i; for(i=1;i<=N;i++) { scanf("%d%lf",&c[i],&w[i]); V+=c[i]; //总金额 } for(i=1;i<=N;i++) for(int j=V;j>=c[i];j--) //倒背包 { dp[j]=max(dp[j],(1-w[i])*dp[j-c[i]]); //状态转移方程,要注意这里逃跑概率要用乘法 } for(i=V;i>=0;i--) //从钱多往钱少的方向遍历 { if(dp[i]>=P) //逃跑概率足够大即可 { printf("%d\n",i); break; } } } return 0; }
G - Cow Exhibition
POJ 2184给出牛的数量和每头牛的智商及幽默感,选出若干头牛使智商总和及幽默感总和为正的情况下智商+幽默感总和的最大值。
难点在于如何保证智商和幽默感同时取到最大。奇淫奇巧:正背包+逆背包
#include"cstdio" #include"iostream" #include"algorithm" #include"cstring" #include"string" #include"cmath" const int INF=-1<<31; int c[1005],w[1005]; //c为智商,w为幽默感 int dp[200005]; int max(int a,int b) { return a>b?a:b; } int main() { int N; while(~scanf("%d",&N)) { memset(c,0,sizeof c); memset(w,0,sizeof w); memset(dp,0,sizeof dp); int i; for(i=0;i<=200000;i++) dp[i]=INF; dp[100000]=0; for(i=1;i<=N;i++) scanf("%d%d",&c[i],&w[i]); for(i=1;i<=N;i++) { if(c[i]<0 && w[i]<0) continue; else if(c[i]>0) { for(int j=200000;j>=c[i];j--) if(dp[j-c[i]]>INF) dp[j]=max(dp[j],dp[j-c[i]]+w[i]); } else { for(int j=c[i];j<=200000+c[i];j++) if(dp[j-c[i]]>INF) dp[j]=max(dp[j],dp[j-c[i]]+w[i]); } } int ans=INF; for(i=100000;i<=200000;i++) //因为区间100000~200000才是表示的整数,那么此时的i就是之前背包中的s[i],如果此时dp[i]也就是f[i]大于等于0的话,我们再加上s[i](此时为i),然后减去作为界限的100000,就可以得到答案 { if(dp[i]>=0) ans=max(ans,dp[i]+i-100000); } printf("%d\n",ans); } return 0; }
相关文章推荐
- 如何组织构建多文件 C 语言程序(二)
- 如何写好 C main 函数
- Lua和C语言的交互详解
- 关于C语言中参数的传值问题
- 简要对比C语言中三个用于退出进程的函数
- 深入C++中API的问题详解
- 基于C语言string函数的详解
- C语言中fchdir()函数和rewinddir()函数的使用详解
- C语言内存对齐实例详解
- C语言编程中统计输入的行数以及单词个数的方法
- C 语言简单加减乘除运算
- C语言自动生成enum值和名字映射代码
- C语言练习题:自由落体的小球简单实例
- 使用C语言判断英文字符大小写的方法
- c语言实现的带通配符匹配算法
- C语言实现顺序表基本操作汇总
- C语言中进制知识汇总
- C语言判断一个数是否是2的幂次方或4的幂次方
- C语言二进制思想以及数据的存储
- C语言中计算正弦的相关函数总结