从AOJ的塔,到POJ的ferry loading,ferry loading到浙江省赛的第7题Process the Tasks,发现这三个问题都是一类比较典型的dp,有必要好好总结一下,避免以后再出现就不会了
这类题目比较原始的版本是
AOJ的塔问题(题目链接)
题意:给你一堆积木,选择其中的某些来组成两个相同高度的塔(对于某块积木,可以放在塔1,可以放在塔2,也可以都不放),问你最大组成的高度是多少?
解析:这个题目不太容易想,我是请教cxlove的(自己太水了),比较常规的想法是用两维表示塔1和塔2的高度,可是这样必然MLE,我们得选取另一个状态来进行DP,这个状态就是两个塔的高度差,dp【i】【j】表示前i个积木用于搭建了两座塔,相差高度为j的时候,两塔的最大高度和。
dp【i+1】【j+block【i+1】】=max(dp【i+1】【j+block【i+1】】,dp【i】【j】+block【i+1】);这个表示i+1块积木放在较高的塔上
dp【i+1】【j-block【i+1】】=max(dp【i+1】【j-block【i+1】】,dp【i】【j】+block【i+1】);这个表示i+1块积木放在较低的塔上
题目数据比较大,必须得用到滚动数组,而且还得继续优化,不然TLE,经过优化,可以达到300ms+。
详细代码见下方。
另一个差不多的题目是poj 2609 ferry loading(题目链接)
题意:给你两个相同车库,每个车库能装M长度的车子,现在给你一个车队(不能改变车队序列的顺序),每辆车子的长度,问你最多能装入多少辆汽车,输出路径
解析:这个题目和上一个题目差不多,不过这里还多了个限制,就是不能总长度超过M*2。
此题,个人能力有限。。没有弄出来输出路径,所以丑陋的代码就不贴了
还有一个题目,就是zoj 3331 浙江省赛的题目(题目链接)
题意:一个加工厂里面有两个机器人,然后有一堆货物需要加工,这些货物加工必须按照顺序来,第n件物品能进行加工的前提条件是n-1件物品加工完成或正在加工。
解析:这个题目相对上面两个题目复杂一点,但还是差不多,dp【i】【j】表示前i件物品加工完了(或i-1件正在加工中)的时候,j表示两个机器人的工作时间差距,dp表示最少耗费的时间。
其中j>0表示a机器人的工作时间更长,j<0表示b机器人工作时间更长
(更新)POJ 1015 (题目链接)
脑子转不过来,数据范围估算错误,不过学习了不少东西
题意:在n个数中选择K个数,使其和的绝对值最小。
解析:因为绝对值范围有限,所以直接开一维表示绝对值,其余的差不多。
状态转移方程比较麻烦,我直接在代码里面附上注释好了,详情见代码
/* aoj 518 塔*/ #include<stdio.h> #include<string.h> #include<stdlib.h> #define INF 1<<29 int dp[2][500010]; int a[51],sum[51]; int cmp(const void *a,const void *b) { return *(int *)a-*(int *)b; } int main() { int n,i,j; int t; while(scanf("%d",&n)!=EOF) { for(i=1;i<=n;i++) scanf("%d",&a[i]); qsort(&a[1],n,sizeof(a[0]),cmp); sum[1]=a[1]; for(i=2;i<=n;i++) sum[i]=sum[i-1]+a[i];//提前算出高度差,然后枚举高度差的时候就不用编立500000了 for(i=0;i<=sum[n];i++) dp[0][i]=-INF; dp[0][0]=0; for(i=0;i<n;i++) { t=(i+1)&1; for(j=0;j<=sum[n];j++) dp[t][j]=-INF; for(j=0;j<=sum[i];j++) { if(dp[t][j+a[i+1]]<dp[i&1][j]+a[i+1])//放在较高的塔 dp[t][j+a[i+1]]=dp[i&1][j]+a[i+1]; if(dp[t][abs(j-a[i+1])]<dp[i&1][j]+a[i+1])//放在较低的塔 dp[t][abs(j-a[i+1])]=dp[i&1][j]+a[i+1]; if(dp[t][j]<dp[i&1][j]) dp[t][j]=dp[i&1][j]; } // printf("I:%d 0:%d 1:%d 2:%d 3:%d\n",i+1,dp[(i+1)&1][0],dp[(i+1)&1][1],dp[(i+1)&1][2],dp[(i+1)&1][3]); } if(dp[n&1][0]/2<=0) printf("-1\n"); else printf("%d\n",dp[n&1][0]/2); } return 0; }
/*zoj 3331 */ #include<iostream> #include<string.h> #define offset 100 #define INF 1<<25 #define min(a,b) ((a)<(b)?(a):(b)) #define get(a) ((a)<0?0:(a)) using namespace std; const int maxn=105; int dp[maxn][maxn*2]; int costa[maxn],costb[maxn]; int n,m; int main() { int i,j,k,T; cin>>T; while(T--) { cin>>n; for(i=1;i<=n;i++) cin>>costa[i]>>costb[i]; for(i=0;i<=n;i++) for(j=0;j<210;j++) dp[i][j]=INF; dp[0][0+offset]=0; for(i=0;i<n;i++) for(j=-100;j<=100;j++) { if(dp[i][j+offset]!=INF) { if(j<=0)//b在上面(机器人b的工作时间比较长) { dp[i+1][-costb[i+1]+offset]=min(dp[i+1][-costb[i+1]+offset],dp[i][j+offset]+costb[i+1]);//放在B上面(继续由机器人b加工 dp[i+1][costa[i+1]+j+offset]=min(dp[i+1][costa[i+1]+j+offset],dp[i][j+offset]+get(j+costa[i+1]));//放在a上面,由机器人A加工 // printf("I:%d J:%d dP:%d\n",i,j,dp[i][j+offset]); } else//a在上面(机器人a的工作时间比较长) { dp[i+1][costa[i+1]+offset]=min(dp[i+1][costa[i+1]+offset],dp[i][j+offset]+costa[i+1]);//放在A上面,继续让机器人A加工 dp[i+1][j-costb[i+1]+offset]=min(dp[i+1][j-costb[i+1]+offset],dp[i][j+offset]+get(costb[i+1]-j));//放在b上面,继续由机器人B加工 // printf("I:%d J:%d dP:%d\n",i,j,dp[i][j+offset]); } } } int ans=INF; for(j=-100;j<=100;j++) if(dp[n][j+offset]<ans) ans=dp[n][j+offset]; cout<<ans<<endl; } return 0; }