现在的位置: 首页 > 综合 > 正文

双塔DP—- 一类以差值为状态表示的DP

2013年08月27日 ⁄ 综合 ⁄ 共 3126字 ⁄ 字号 评论关闭

从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;
}

抱歉!评论已关闭.