第一题:
HDU2546 饭卡
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2546
题目意思:中文题目,不解释题意
思路:选出最贵的一道菜和其他菜分离出来,然后把预算减去5元,以剩下的预算和剩下的菜进行0-1背包,然后将M-dp[M-5]-maxdish就是答案。
代码:
1 //Author: xiaowuga 2 #include <iostream> 3 #include <algorithm> 4 #include <set> 5 #include <vector> 6 #include <queue> 7 #include <cmath> 8 #include <cstring> 9 #include <cstdio> 10 #include <ctime> 11 #include <map> 12 #include <bitset> 13 #include <cctype> 14 #define maxx INT_MAX 15 #define minn INT_MIN 16 #define inf 0x3f3f3f3f 17 #define mem(s,ch) memset(s,ch,sizeof(s)) 18 #define nc cout<<"nc"<<endl 19 #define sp " " 20 const long long N=100000; 21 using namespace std; 22 typedef long long LL; 23 typedef int II; 24 II n,M; 25 II dish[N]; 26 II d[N]; 27 II ct=1; 28 II dp[N]; 29 II maxdish; 30 void DP(){ 31 II V=M-5; 32 mem(dp,0); 33 for(II i=1;i<ct;i++) 34 for(II j=V;j>=d[i];j--){ 35 dp[j]=max(dp[j],dp[j-d[i]]+d[i]); 36 } 37 } 38 int main() { 39 ios::sync_with_stdio(false);cin.tie(0); 40 while(cin>>n&&n){ 41 maxdish=-inf; 42 ct=1; 43 II pos; 44 for(II i=1;i<=n;i++){ 45 cin>>dish[i]; 46 if(maxdish<dish[i]){ 47 maxdish=dish[i]; 48 pos=i; 49 } 50 } 51 for(int i=1;i<=n;i++){ 52 if(i!=pos) d[ct++]=dish[i]; 53 } 54 cin>>M; 55 if(M<5){ 56 cout<<M<<endl;continue; 57 } 58 DP(); 59 cout<<M-dp[M-5]-maxdish<<endl; 60 } 61 return 0; 62 }
第二题:
UVA624 CD
题目链接:https://vjudge.net/problem/UVA-624
题目意思:非常常规的0-1背包,唯一的就是需要打印路径,采用的是递归打印的方法
代码:
1 //Author: xiaowuga 2 #include <iostream> 3 #include <algorithm> 4 #include <set> 5 #include <vector> 6 #include <queue> 7 #include <cmath> 8 #include <cstring> 9 #include <cstdio> 10 #include <ctime> 11 #include <map> 12 #include <bitset> 13 #include <cctype> 14 #define maxx INT_MAX 15 #define minn INT_MIN 16 #define inf 0x3f3f3f3f 17 #define mem(s,ch) memset(s,ch,sizeof(s)) 18 #define nc cout<<"nc"<<endl 19 #define sp " " 20 const long long N=100000; 21 using namespace std; 22 typedef long long LL; 23 typedef int II; 24 II V,n,d[N]; 25 II dp[N]; 26 II ans[N],ct; 27 II path[100][N]; 28 void DP(){ 29 mem(dp,0); 30 mem(path,0); 31 for(II i=1;i<=n;i++) 32 for(II j=V;j>=d[i];j--){ 33 if(dp[j]<=(dp[j-d[i]]+d[i])){ 34 dp[j]=dp[j-d[i]]+d[i]; 35 path[i][j]=1; 36 } 37 } 38 } 39 void printpath(II x,II m){ 40 if(x==0) return; 41 if(path[x][m]==0) printpath(x-1,m); 42 else{ 43 printpath(x-1,m-d[x]); 44 ans[ct++]=d[x]; 45 } 46 } 47 int main() { 48 ios::sync_with_stdio(false);cin.tie(0); 49 while(cin>>V){ 50 cin>>n; 51 for(II i=1;i<=n;i++) cin>>d[i]; 52 DP(); 53 ct=0; 54 printpath(n,V); 55 for(II i=0;i<ct;i++){ 56 cout<<ans[i]<<sp; 57 } 58 cout<<"sum:"<<dp[V]<<endl; 59 } 60 return 0; 61 }
第三题:
UVA562 Dividing coins
题目链接:https://vjudge.net/problem/UVA-562
题目意思:0-1背包的问题的一个变形,平衡问题,将n个硬币的总价值累加得到sum,再用sum/2作为背包容量对n个硬币做01背包处理,看能组成的最大容量是多少,使得分开的两堆硬币,价值的差最接近。
代码:
1 //Author: xiaowuga 2 #include <iostream> 3 #include <algorithm> 4 #include <set> 5 #include <vector> 6 #include <queue> 7 #include <cmath> 8 #include <cstring> 9 #include <cstdio> 10 #include <ctime> 11 #include <map> 12 #include <bitset> 13 #include <cctype> 14 #define maxx INT_MAX 15 #define minn INT_MIN 16 #define inf 0x3f3f3f3f 17 #define mem(s,ch) memset(s,ch,sizeof(s)) 18 #define nc cout<<"nc"<<endl 19 #define sp " " 20 const long long N=100000; 21 using namespace std; 22 typedef long long LL; 23 typedef int II; 24 II n,d[N],dp[N]; 25 II sum=0; 26 void DP(){ 27 mem(dp,0); 28 II V=sum/2; 29 for(II i=1;i<=n;i++) 30 for(II j=V;j>=d[i];j--){ 31 dp[j]=max(dp[j],dp[j-d[i]]+d[i]); 32 } 33 cout<<sum-2*dp[V]<<endl; 34 } 35 int main() { 36 ios::sync_with_stdio(false);cin.tie(0); 37 II T; 38 cin>>T; 39 while(T--){ 40 cin>>n; 41 sum=0; 42 for(II i=1;i<=n;i++){ 43 cin>>d[i]; 44 sum+=d[i]; 45 } 46 DP(); 47 } 48 return 0; 49 }
第四题:
HDU2955 Robberies
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2955
题目意思:有一个强盗要去几个银行偷盗,他既想多投点钱,又想尽量不被抓到。已知各个银行的金钱数和被抓的概率,以及强盗能容忍的最大被抓概率。求他最多能偷到多少钱?
题目思路:原先想的是把概率当做背包,在这个范围内最多能抢多少钱,但是wa了,问题在于最大不被抓概率不是简单的累加。而是p = (1-p1)(1-p2)(1-p3) 其中p为最大不被抓概率,p1,p2,p3为各个银行被抓概率。现在是以抢到的钱为背包,求最大的被抓概率。从大到小枚举背包, 第一个被抓概率小于可容忍程度的的背包容量就是答案,个人感觉非常好的一道题。
代码:
1 //Author: xiaowuga 2 #include <iostream> 3 #include <algorithm> 4 #include <set> 5 #include <vector> 6 #include <queue> 7 #include <cmath> 8 #include <cstring> 9 #include <cstdio> 10 #include <ctime> 11 #include <map> 12 #include <bitset> 13 #include <cctype> 14 #define maxx INT_MAX 15 #define minn INT_MIN 16 #define inf 0x3f3f3f3f 17 #define mem(s,ch) memset(s,ch,sizeof(s)) 18 #define nc cout<<"nc"<<endl 19 #define sp " " 20 const long long N=10000+10; 21 using namespace std; 22 typedef long long LL; 23 typedef int II; 24 II w[N]; 25 double g[N],P,dp[N]; 26 II n; 27 II sum=0; 28 void DP(){ 29 memset(dp,0,(sum+1)*sizeof(dp[0])); 30 dp[0]=1; 31 for(II i=1;i<=n;i++) 32 for(II j=sum;j>=w[i];j--){ 33 double t=(1.0-g[i])*dp[j-w[i]]; 34 dp[j]=max(dp[j],t); 35 } 36 II x; 37 for(x=sum;1-dp[x]>=P;x--); 38 cout<<x<<endl; 39 return ; 40 } 41 int main() { 42 ios::sync_with_stdio(false);cin.tie(0); 43 II T; 44 cin>>T; 45 while(T--){ 46 cin>>P>>n; 47 sum=0; 48 for(II i=1;i<=n;i++){ 49 cin>>w[i]>>g[i]; 50 sum+=w[i]; 51 } 52 if(P<=1-1e-8){ 53 DP(); 54 } 55 else{ 56 cout<<sum<<endl; 57 } 58 } 59 return 0; 60 }
第五题:
POJ2184 Cow Exhibition
题目链接:http://poj.org/problem?id=2184
题目意思:这是又是一道01背包的变体,题目要求选出一些牛,使smartness和funness值的和最大,而这些牛有些smartness或funness的值是负的,还要求最终的smartness之和以及funness之和不能为负。
题目思路:一是将smartness看作花费、将funness看作价值,从而转化为01背包;二是对负值的处理,引入一个shift来表示“0”,这里的shift一定要大于每一个smartness的绝对值。
一遇到负值我们就懵逼了,实际上负值也可以加入背包,整个背包的容量为N,0为左端点,N为右端点,shift为中点(实际意义上的零点)。
当花费为正的时候,假设花费为x,那么区间【x,N】内的任意状态i都有可能并可以从i-x转移而来,而区间【0,x)则不可能,因为向他们转移的状态超出边界了,大状态从小状态转移而来,所以我们先更新大状态再更新小状态。所以反向枚举。
当花费为负的时候,假设花费为x(x为负数),那么区间【0,N+x】内任意状态i都可以并可能从i-x转移而来,而区间(N-x,N】则不行,因为向他们转移的状态都超出边界了。由于小状态是由大状态转移而来,先更新小状态在更新大状态,所以正向枚举。
最后由于题目要求花费得是正数,价值也要是正数,所以遍历区间【shift,N】(shift是实际意义上的零点),去寻找下标(真实下标为i-shift)+dp[i]的最大值,就是符合题目意思的答案。
总结:个人认为是一道好题,把各种限制转化为背包的问题,通过背包的性质将其一一解决,说明对于和最大问题背包也是一个可以思考的方向。
代码:
1 //Author: xiaowuga 2 #include <iostream> 3 #include <algorithm> 4 #include <set> 5 #include <vector> 6 #include <queue> 7 #include <cmath> 8 #include <cstring> 9 #include <cstdio> 10 #include <ctime> 11 #include <map> 12 #include <bitset> 13 #include <cctype> 14 #define maxx INT_MAX 15 #define minn INT_MIN 16 #define inf 0x3f3f3f3f 17 #define mem(s,ch) memset(s,ch,sizeof(s)) 18 #define nc cout<<"nc"<<endl 19 #define sp " " 20 const long long N=200000+100; 21 using namespace std; 22 typedef long long LL; 23 typedef int II; 24 II dp[N]; 25 II w[N],v[N]; 26 II n; 27 II shift; 28 void DP(){ 29 for(II i=1;i<=n;i++){ 30 if(w[i]>=0){ 31 for(II j=N-1;j>=w[i];--j){ 32 dp[j]=max(dp[j],dp[j-w[i]]+v[i]); 33 } 34 } 35 else{ 36 for(II j=0;j<N+w[i];j++){ 37 dp[j]=max(dp[j],dp[j-w[i]]+v[i]); 38 } 39 } 40 } 41 LL ans=0; 42 for(II i=shift;i<N;i++){ 43 if(dp[i]>=0&&i-shift+dp[i]>ans){ 44 ans=i-shift+dp[i]; 45 } 46 } 47 cout<<ans<<endl; 48 } 49 int main() { 50 ios::sync_with_stdio(false);cin.tie(0); 51 while(cin>>n){ 52 for(II i=1;i<=n;i++) cin>>w[i]>>v[i]; 53 mem(dp,-inf); 54 shift=100000; 55 dp[shift]=0; 56 DP(); 57 } 58 return 0; 59 }
第六题:
HDU2639 Bone Collector II
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2639
题目意思:求K大背包,某个状态的下第k大价值
思路:多开一维空间来记录k个状态,即每个状态有k个值。每次转移的时候实际上转移态的k个状态想被转移态的k个状态发生转移,去前k个不同的值,思路很简单,感觉代码很有意思。
代码:
1 //Author: xiaowuga 2 #include <iostream> 3 #include <algorithm> 4 #include <set> 5 #include <vector> 6 #include <queue> 7 #include <cmath> 8 #include <cstring> 9 #include <cstdio> 10 #include <ctime> 11 #include <map> 12 #include <bitset> 13 #include <cctype> 14 #define maxx INT_MAX 15 #define minn INT_MIN 16 #define inf 0x3f3f3f3f 17 #define mem(s,ch) memset(s,ch,sizeof(s)) 18 #define nc cout<<"nc"<<endl 19 #define sp " " 20 const long long N=1000*2; 21 using namespace std; 22 typedef long long LL; 23 typedef int II; 24 II dp[N][33],val[N],vol[N],A[33],B[33]; 25 II n,v,k; 26 void DP(){ 27 II a,b,c; 28 for(II i=1;i<=n;i++) 29 for(II j=v;j>=vol[i];j--){ 30 for(II kk=1;kk<=k;kk++){ 31 A[kk]=dp[j-vol[i]][kk]+val[i]; 32 B[kk]=dp[j][kk]; 33 } 34 A[k+1]=-1;B[k+1]=-1; 35 a=b=c=1; 36 while(c<=k&&(A[a]!=-1||B[b]!=-1))( 37 if(A[a]>B[b]) dp[j][c]=A[a++]; 38 else dp[j][c]=B[b++]; 39 40 if(dp[j][c]!=dp[j][c-1]) c++; 41 } 42 } 43 cout<<dp[v][k]<<endl; 44 } 45 46 int main() { 47 ios::sync_with_stdio(false);cin.tie(0); 48 II T; 49 cin>>T; 50 while(T--){ 51 cin>>n>>v>>k; 52 for(II i=1;i<=n;i++) cin>>val[i]; 53 for(II i=1;i<=n;i++) cin>>vol[i]; 54 mem(dp,0); 55 DP(); 56 } 57 return 0; 58 }
第七题:
POJ2923 Relocation
题目链接:http://poj.org/problem?id=2923
题目意思:有 n 个货物,并且知道了每个货物的重量,有两辆车,载重量分别为c1,c2,两辆车必须同时出发,同时回来,不能说哪辆车单独出发,问最少需要运送多少次可以将货物运完。
分析: 找出所有状态(1.....(1<<n)-1)中选出可以用两辆车一次运完的状态,然后我们通过没有交集状态之间的0-1背包找出,全部都运走的状态下。最少需要多少次。
其中判断两个货物是否可以一起被运走的方法也十分有意思。不好说清楚看代码应该很容易理解。就是从差不多通过组合C1可以满足容量,且剩余重量小于C2的载重量,然后把所有满足的枚举作为物品。
然后以状态为背包,判断在满足某个状态下最少的步数。其中有交集的状态不能转移。位运算或代表状态相加,还有j先j&a[i]发生转移的时候,需要注意j是否已经可以从j从0转移过来。(也就是dp[j]!=inf,所以这道题一开始需要把数组全部初始化为inf,把dp[0]初始化为0)。
代码:
1 //Author: xiaowuga 2 #include <iostream> 3 #include <algorithm> 4 #include <set> 5 #include <vector> 6 #include <queue> 7 #include <cmath> 8 #include <cstring> 9 #include <cstdio> 10 #include <ctime> 11 #include <map> 12 #include <bitset> 13 #include <cctype> 14 #define maxx INT_MAX 15 #define minn INT_MIN 16 #define inf 0x1f1f1f1f 17 #define mem(s,ch) memset(s,ch,sizeof(s)) 18 #define nc cout<<"nc"<<endl 19 #define sp " " 20 const long long N=150; 21 using namespace std; 22 typedef long long LL; 23 typedef int II; 24 II dp[1<<11]; 25 II a[1<<11]; 26 II v[1<<11]; 27 II c1,c2,n; 28 II w[N]; 29 II st,top; 30 bool OK(II x){ 31 II s=0; 32 memset(v,0,sizeof(v)); 33 v[0]=1; 34 for(II i=0;i<n;i++){ 35 if((1<<i)&x){ 36 s+=w[i]; 37 if(s>c1+c2) return false; 38 for(II j=c1;j>=w[i];j--){ 39 if(v[j-w[i]]) v[j]=1; 40 } 41 } 42 } 43 for(II i=0;i<=c1;i++) 44 if(s-i<=c2&&v[i]) return true;; 45 return false; 46 } 47 int main() { 48 ios::sync_with_stdio(false);cin.tie(0); 49 II ca=1; 50 II T; 51 cin>>T; 52 while(T--){ 53 cin>>n>>c1>>c2; 54 for(II i=0;i<n;i++) cin>>w[i];//读入 55 memset(dp,inf,sizeof(dp)); 56 dp[0]=0;top=0; 57 st=(1<<n)-1; 58 top=0; 59 for(II i=1;i<=st;i++) if(OK(i)) a[top++]=i; 60 for(II i=0;i<top;i++) 61 for(II j=st;j>=0;j--){ 62 if(dp[j]!=inf){//首先j状态要存在才能向别人转移 63 if(j&a[i]) continue;//有冲突 64 dp[a[i]|j]=min(dp[a[i]|j],dp[j]+1); 65 } 66 } 67 cout<<"Scenario #"<<ca++<<":"<<endl; 68 cout<<dp[st]<<endl<<endl; 69 } 70 return 0; 71 }