P188 购物问题
题目梗概:
n个物品,其中每个物品价格xi,但是某两个物品不能同时购买。
问最大的价格是多少?
思考与理解:
一开始并没有想到树形背包DP,只是一直在想是不是分组背包~
在之后瞅了瞅题解的思路之后,恍然大悟。
先把有限制的物品之间的关系转换为父子关系就可以进行DP了。
对于每个有限制的物品要不选 要么不选 选的话会有什么结果 不选的话有什么结果。
如果没有限制的话 那么肯定是要买的~
#include <cstdio> #include <algorithm> #include <cstring> #define up(a,b,c) for(register int c=a;c<=b;++c) int n,m; int dp[1005][3]; int maodun[1005][1005],value[1005],ans,son[1005][1005]; bool used[1005]; void dfs(int x){ used[x] = true; up(1,maodun[x][0],i){ if(!used[maodun[x][i]]){ son[x][++son[x][0]] = maodun[x][i]; dfs(maodun[x][i]); } } } void RunDp(int x){ if(son[x][0]==0){ dp[x][1]=value[x]; dp[x][0]=0; } else{ up(1,son[x][0],i) RunDp(son[x][i]); up(1,son[x][0],i){ int v = son[x][i]; dp[x][1]+=dp[v][2]; dp[x][2]+=std::max(dp[v][1],dp[v][2]); } dp[x][1]+=value[x]; } } int main(){ scanf("%d%d",&n,&m); up(1,n,i) scanf("%d",&value[i]); up(1,m,i){ int x,y; scanf("%d%d",&x,&y); maodun[x][++maodun[x][0]] = y; maodun[y][++maodun[y][0]] = x; } up(1,n,i){ if(maodun[i][0]==0){ ans+=value[i]; used[i] = 1; } else{ if(!used[i]){ dfs(i); RunDp(i); ans+=std::max(dp[i][1],dp[i][2]); } } } printf("%d\n",ans); return 0; }
P188
P201 奥运大包围
题目梗概:
(题面什么鬼)问题就是求一个环从某个地方断开,然后求LIS,取LIS最小值即可.
思考:
LIS挺简单的,但必须用nlogn的二分做法,否则n^3会TLE。
#include<string.h> #include<stdio.h> #include<iostream> #include<algorithm> using namespace std; int dp[10001]; int num[10001]; int a[20001]; int nums; int ans; void cha(int x) { if(nums==0||x>=num[nums-1]) { num[nums++]=x; return ; } int l,r,mid; l=0,r=nums; mid=(l+r)/2; while(l<r) { if(num[mid]<=x)l=mid+1; else if(num[mid]>x) r=mid; mid=(l+r)/2; } num[mid]=x; } void dos(int l,int r) { nums=0; for(int i=l; i<r; i++) { cha(a[i]); } ans=min(ans,nums); } int main() { int n,i; scanf("%d",&n); ans=n; for(i=0; i<n; i++) { scanf("%d",&a[i]); } for(i=n; i<2*n; i++) { a[i]=a[i-n]; } for(i=0; i<n; i++) { dos(i,i+n); } cout<<ans<<endl; return 0; }
P201
P202 奥运火炬登珠峰
题面梗概:
n个物品,每个物品都有一个x氧气含量,y氮气含量,z重量。
给出保障生命安全的a,b,问z的最小重量是多少。
思考:
普通的一个二位费用背包,没啥说的。只不过需要把上限a,b开大点。毕竟选的x,y可以超过这个下限a,b
#include <cstdio> #include <algorithm> #include <cstring> int dp[233][1005]; int A,T,n; int O[1005],N[1005],G[1005]; int Out=0x3f3f3f3f; int main(){ memset(dp,0x3f3f3f3f,sizeof(dp)); scanf("%d%d",&A,&T); scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%d%d%d",&O[i],&N[i],&G[i]); dp[0][0]=0; for(int i=1;i<=n;i++){ for(int j=A+A;j>=O[i];j--){ for(int k=T+T;k>=N[i];k--){ dp[j][k]=std::min(dp[j][k],dp[j-O[i]][k-N[i]]+G[i]); //Out=std::min(Out,dp[j][k]); } } } for(int i=A+A;i>=A;i--){ for(int j=T+T;j>=T;j--){ Out=std::min(Out,dp[i][j]); //if(dp[i][j]==129) printf("%d %d\n",i,j); } } printf("%d\n",Out); return 0; }
P202
P671 纯洁的买卖
题面梗概:
猪脚有m元经费,他会以x买入某件物品,以y的价格卖出。问最后会有多少钱。
思考:
背包问题,只不过需要处理一下xy的关系。
#include <cstdio> #include <algorithm> #include <cstring> int n,m; int dp[1000005]; int x,y; int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++){ scanf("%d%d",&x,&y); y-=x*2; for(int j=x;j<=m;j++){ if(j-x>=0)dp[j]=std::max(dp[j],dp[j-x]+y); //printf("dp[%d]:%d\n",j,dp[j]); } } printf("%d\n",dp[m]+m); return 0; }
P671
P57 找啊找啊找GF
题面梗概:
n个MM,每个MM需要花费rmb,rp,time,猪脚有m块RMB,r的人品值。
在保证MM数量的前提下,最小花费时间是多少。
思考:
一开始想着只用一个二维费用背包搞,记录MM数量最大是多少,然后在这个数量中查找一个最小值时间。
但是发现莫名其妙WA了,看了看题解。
发现做法很妙:
如果MM数量多,就保留多的。
如果MM数量一样,那么久保留时间短的。
#include <cstdio> #include <cstring> #include <algorithm> int n,m,r; int dp[105][105]; int TIME[105][105]; int x[105],y[105],z[105]; int Min=0x3f3f3f3f; int main(){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d%d%d",&x[i],&y[i],&z[i]); } scanf("%d%d",&m,&r); for(int i=1;i<=n;i++){ for(int j=m;j>=x[i];j--){ for(int k=r;k>=y[i];k--){ if(dp[j][k] < dp[j-x[i]][k-y[i]]+1){ dp[j][k] = dp[j-x[i]][k-y[i]]+1; TIME[j][k] = TIME[j-x[i]][k-y[i]]+z[i]; } if(dp[j][k] == dp[j-x[i]][k-y[i]]+1 && TIME[j][k] > TIME[j-x[i]][k-y[i]]+z[i]){ TIME[j][k] = TIME[j-x[i]][k-y[i]]+z[i]; } //printf("dp[%d][%d]:%d\n",j,k,dp[j][k]); } } } printf("%d",TIME[m][r]); return 0; }
p57
P140 分配时间
题面梗概:
n个科目,t时间。
每个科目花费不同的时间可以得到不同的分数,但每门科目需要之前写名字花费x时间。
问最多可以拿到几分?
思考:
分组背包,只不过需要特出处理一下写名字的时间。
#include <cstdio> #include <algorithm> #include <cstring> int T,n,name; int num[23][233]; int dp[233333]; int main(){ scanf("%d%d%d",&T,&n,&name); //读入 for(int i=1;i<=n;i++){ for(int j=1;j<=T;j++){ scanf("%d",&num[i][j+name]); } } for(int k=1;k<=n;k++){ for(int i=T;i>=name;i--){ for(int j=i;j>=name;j--){ dp[i] = std::max(dp[i],dp[i-j]+num[k][j]); } } } printf("%d\n",dp[T]); return 0; }
P140
P169 最小乘车费用
题面梗概:
每个车最多可以行驶10公里,行驶1,2,3,4...10公里的费用各不相同。
问行驶x公里的,最小费用是多少。
思考:
背包问题.
#include <cstdio> #include <algorithm> #include <cstring> int n,num[105]; int dp[233]; int main(){ memset(dp,0x3f3f3f3f,sizeof(dp)); for(int i=1;i<=10;i++) scanf("%d",&num[i]),dp[i]=num[i]; scanf("%d",&n); for(int i=1;i<=n;i++){ for(int j=1;j<=10;j++){ if(i>j) dp[i]=std::min(dp[i],dp[i-j]+num[j]); else break; //printf("dp[%d]:%d\n",i,dp[i]); } } printf("%d\n",dp[n]); return 0; }
P169
P95 多多看DV(加强版)
题面梗概:
爷爷给出T单位时间,商店有n个碟片,叔叔必须买m个(不能多不能少),输入的每个碟片会花费x的时间,会得到y的快乐。
如果碟片没法在规定时间内看完输出0,否则输出今晚的总分.
思考:
题面需要转化一下,把时间当作一个物品属性。进行二维费用背包。
贪心的思考一下:如果n个物品中,m个物品的时间>T的话。直接输出0
#include <cstdio> #include <algorithm> #include <cstring> int n,limit,l; int t[105],m[105],fuck[105],Can[105]; int dp[233333][233]; int main(){ memset(dp,-0x3f3f3f3f,sizeof(dp)); scanf("%d%d%d",&n,&limit,&l); for(int i=1;i<=n;i++){ scanf("%d%d",&t[i],&m[i]); Can[i]=t[i]; fuck[i]=1; } std::sort(Can+1,Can+1+n); int ans=0; for(int i=1;i<=limit;i++){ ans+=Can[i]; } if(ans>l){ printf("0\n"); return 0; } for(int i=0;i<=l;i++) dp[i][0]=0; for(int i=1;i<=n;i++){ for(int j=l;j>=t[i];j--){ for(int k=limit;k>=1;k--){ dp[j][k] = std::max(dp[j][k],dp[j-t[i]][k-1]+m[i]); } } } printf("%d\n",dp[l][limit]); return 0; }
P95