这是一道经典的双塔DP题,建议没学过双塔的人先去学了再来弄这题吧.
最初始的双塔题好像是ZOJ上的那道------搭建双塔
但我学了把那道题弄懂后,再来写这道题的时候,还是没有任何思路,可能是我蠢爆了........
无奈之下我翻了好几个博客去找题解,但几乎所有博客上讲的都有点粗枝大叶的,不够详细,我还得自己走几下代码,才能弄懂大致的思路,其中我看的一个博客,将大致的思路讲清楚了.
ZOJ 3331 Process the Tasks(双塔DP)_Dacc123的博客-CSDN博客
可以去翻一翻
dp[i][j] 表示执行第i个任务,机器A和机器B的时间差
双塔DP 顾名思义可以把A机器和B机器看作是两座塔,如果把当前任务放在哪个塔上,那么哪个塔的高度就会增加
每次放任务,都会有两个选择,要么放在A塔,要么放在B塔。如果放在高的塔上,根据题意在同一个机器上必要上一个任务完成
时才能进行下一个任务,所以等高的塔完成上一个任务是,A,B都是空闲的了,这个是在高的塔上放任务,时间差就是这个任务的时间
如果放在低的塔上,那么在之前的差值上加上这个任务的时间
这段文字贵为重要,代码里if语句中写的四行状态转移方程都是在这段文字的理解上弄得,但各方面细节讲的不够清晰,我在这里稍微弄清一下思路,方便ACM里来学双塔的人方便一点.
好了直接上代码把
#include <bits/stdc++.h>
using namespace std;
const int inf=0x3f3f3f3f;
int dp[105][205];
int main()
{
int t;cin>>t;
while(t--)
{
int n,ta,tb;
cin>>n;
memset(dp,inf,sizeof(dp));
dp[0][100]=0;
for(int i=1;i<=n;i++)
{
cin>>ta>>tb;
/*首先我要说明一下,为什么j要分为正和负两种情况来做,
因为双塔DP中dp[i][j]中的j记录的是两个塔之间的高度差,
由于这里两个塔往上搭同一块积木所产生的高度不同(也就是两个机器做同一个任务所用时间不同)
所以要以100为分界线,100一下表示B塔为高塔,100以上表示A为高塔
为什么要以100为分界线呢?题目中给出了每个ta,tb的范围,所以差值绝不超过100;
*/
for(int j=-99;j<=99;j++)
{
if(dp[i-1][j+100]==inf) continue;
if(j<0)//j<0的时候说明此时高塔为B
{
dp[i][100-tb]=min(dp[i][100-tb],dp[i-1][100+j]+tb);
//这里由于B为高塔,那么[100-tb]表示把这个任务放在高塔上来做
dp[i][j+ta+100]=min(dp[i][j+ta+100],dp[i-1][100+j]+max(0,j+ta));
//这里A为低塔,[j+ta+100]那么说明是在原本差值为j的情况下给A塔来做这个任务
//后面有个max是因为,你把这个任务给A塔后,后产生两种情况
//第一种,A塔还是比B塔低,说明高塔还是高塔,没有变得更高.
//第二种,A塔比B塔高了,那么高出多少?高出了j+ta(因为j为负的)
}
else//此时说明高塔为A
{//这里上同,不再赘述了
dp[i][ta+100]=min(dp[i][ta+100],dp[i-1][j+100]+ta);
dp[i][j-tb+100]=min(dp[i][j-tb+100],dp[i-1][j+100]+max(0,tb-j));
}
}
}
int res=inf;
for(int i=-99;i<=99;i++)
res=min(res,dp[n][i+100]);
cout<<res<<endl;
}
return 0;
}