zju 1013 dp

  ps:zoj的1013这题算是我接触过最为麻烦的dp题了,一开始想用以往解背包问题的方法把题目过掉。。很遗憾~哥还是太连清了~╮(╯▽╰)╭。。。WA了有两天了吧。。纠结致死啊就~到网上搞了篇某大牛的code。。。分析半天。。。震惊啊~~~彻底orz。。。



 下面贴篇鄙人蛋疼的翻译。。。委屈  

  很久以前,有一位Enroth国的女王叫凯瑟琳。一天她得知了他父亲的死讯,因此她要渡海去 Erathia 参加他父亲的葬礼。
  为了防止意外情况,她组建了一艘军舰来护送她。
  在到达Erathia 的海岸时,凯瑟琳发现了一座盟军废弃的向导灯塔。在这里她得知一个黑暗骑士用一杯毒酒毒死他的父亲,Erathia 也陷入了敌人的手里。之后她召集了当地的军队,向Erathia的城堡示威,打算收复失地。


  在这场战中,她发现士兵们迫切需要装备。她很清楚好装备都来自矮人的工作坊。这个工作坊里的装备闻名于它们的坚固和顶级质量。"不死指望的斗篷","死神的铠甲","天使XX" 都是其中的绝品。但是,不幸地,这个工作坊已经被Erathia的城堡收购了。因此她派遣勇士去城堡向矮人寻求帮助。
  “这是我们的荣幸帮助正义之师,”工坊的领袖瑞恩回信说道“但我们没有足够的资源来打造这些绝品,因此我们打算试着尽可能多地提供普通的装备,当然这里也有跟好的装备来自其他的工坊。但是,我们正面临一个难题。城堡正在受到围攻,装备禁止从大门运送出城堡。我们不得不向商队寻求帮助,正如你所知的,每一支商队的运货能力是限定好的。如果他们运输稍微超出一点点,他们会被警卫发现,然后被毒死。影刺你们不得不安排好怎么来运输这些装备。”
  工坊会提供头盔、铠甲、战靴,这三个有不同的防御力。当然,他们的重量和尺寸每个都是不一样的。其他的,瑞恩特告诉凯瑟琳是否装备这三个一起,它们会组成一组装备,会提供更多防御力。至于这些被提到的商队,每一支都有它自己的负重和尺寸的限制。凯瑟琳不得不决定如何安排运输方案,使他的士兵尽可能有更多战斗力。你能帮助她完成这个方案么?


  INPUT
  输入有多个方案,每个测试数据的第一行包含一个n,表示商队的数量(1,100),接下来的四行,描述装备的信息。第一行包含3个整数,w1,s1,d1暗示头盔的重量,尺寸和防御力 ,w2,s2,d2暗示铠甲的重量,尺寸和防御力 , w3,s3,d3暗示战靴的重量,尺寸和防御力 ,第四行包括四个整数c1,c2,c3,d4表示套装中头盔,铠甲,战靴的数量,d4表示套装的防御力。
  接下来n行,描述各商队的重量限制和尺寸限制。
  输入n为0时结束。
  
  OUTPUT
  输出最佳方案时地防御能力。



Sample Input


3
1 1 3
5 6 10
2 1 2
1 1 1 50
1 1
5 6
2 1
0


Output for the Sample Input

Case 1: 50


  
#include <iostream>
#include <stdio.h>
#include <memory.h>
using namespace std;
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))

int f[2][510][510],w[4],s[4],d[4],x[110],y[110],n;

int main()
{
	int i,j,k,now,pre,tem,c1,c2,c3,d4,ca=0,p,q,rew,res;
	int last1,last2,up1,up2;
	while(scanf("%d",&n)&&n)
	{
		if(ca>0) printf("\n");
		for(i=1;i<=3;i++) scanf("%d%d%d",&w[i],&s[i],&d[i]);
		scanf("%d%d%d%d",&c1,&c2,&c3,&d4);
		for(i=1;i<=n;i++) scanf("%d%d",&x[i],&y[i]);
		memset(f,-1,sizeof(f));
           

		f[0][0][0]=0;
		now=1; pre=0; 
		last1=0; last2=0;


		for(i=1;i<=n;i++)
		{
			up1=last1+min(x[i]/w[1],y[i]/s[1]);  // 加点小优化减少决策范围 从5000ms减到500ms
			if(up1>500) up1=500;
			up2=last2+min(x[i]/w[2],y[i]/s[2]); 
			if(up2>500) up2=500;


			for(j=0;j<=up1;j++)
			{
				for(k=0;k<=up2;k++)
				{
					f[now][j][k]=-1;



					for(p=0;p<=j;p++)
					{
						if(p*w[1]>x[i]||p*s[1]>y[i]) break;
						for(q=0;q<=k;q++)
						{
							if(p*w[1]+q*w[2]>x[i]||p*s[1]+q*s[2]>y[i]) break;
							if(f[pre][j-p][k-q]==-1) continue;
							rew=x[i]-p*w[1]-q*w[2];
							res=y[i]-p*s[1]-q*s[2];
							tem=min(rew/w[3],res/s[3]); // 当前这辆车装完p个武器1和q个武器2后还能装武器3的个数
							if(f[pre][j-p][k-q]+tem>f[now][j][k])
								f[now][j][k]=f[pre][j-p][k-q]+tem;
						}
					}






					if(f[now][j][k]!=-1)
					{
						last1=max(last1,j);
						last2=max(last2,k);
					}
				}
			}
			tem=now; now=pre; pre=tem;
		}
		int flag,ans=0,num;
		if(c1*d[1]+c2*d[2]+c3*d[3]>d4) flag=1;  // 选择比较好的组合武器的方式
		else flag=2;
		for(i=0;i<=500;i++)
		{
			for(j=0;j<=500;j++)
			{
				if(f[pre][i][j]==-1) continue;
				k=f[pre][i][j];
				if(flag==1)
					ans=max(ans,i*d[1]+j*d[2]+k*d[3]);
				else
				{
					num=min(i/c1,min(j/c2,k/c3));
					tem=num*d4+(i-num*c1)*d[1]+(j-num*c2)*d[2]+(k-num*c3)*d[3];
					ans=max(ans,tem);
				}
			}
		}
		printf("Case %d: %d\n",++ca,ans);
	}
	return 0;
}



  解法中的五重循环或许有点唬人,其实就是平常DP中的两重循环的拓展:前两重循环遍历表示出状态点,后两重循环遍历表示出决策点,然后利用状态转移方程

  f[now][j][k] = max{ f[now][j][k] ,f[pre][j-p][k-q]+tem},弄出一张最优表;当然,此题中由于有多个背包,所以会有多张表,这里用now和pre进行滚动,达到两两合并表的效果。
 最后的两个for循环遍历一遍,找出最大值。。。
 总之,大牛的代码实在太亮了。。。感觉自己以前写的代码都是垃圾额。。。╮(╯▽╰)╭。。。



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值