hdu 4778 Gems Fight!

题目链接:hdu 4778

         状压DP

         用DP[i]表示从i状态选到结束得到的最大值

/******************************************************
 * File Name:   b.cpp
 * Author:      kojimai
 * Creater Time:2014年08月13日 星期三 11时42分53秒
******************************************************/
/*
 *有g种颜色的宝石,在一个容器中每s个同色宝石可以合成一个魔法石,给你b个包,里面有一定数目的宝石。
 *两人博弈,每个人每个回合把一个包中的所有的宝石放进容器中,如果该操作能得到一个魔法石,则能再进行一次操作
 *两个人都采取最有策略,问最终先手得到的魔法石与后手得到的魔法石的差值为多少

 *状压DP,dp[i]表示i状态为起始,选到结束能得到的最大值
 *i&(1<<j)==0 表示当前状态下j已经选过了
 *i&(1<<j)==1 表示当前状态下j可选,转移方程:
 **dp[i]=max(dp[i],dp[i^(1<<j)]+cnt) 在i^(1<<j)状态下选j能得到cnt个魔法石
 **dp[i]=max(dp[i],-dp[i^(1<<j)])    选了j之后得不到魔法石
*/
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>
using namespace std;
#define FFF -23333333
int gem[22][9];//每个包中的宝石
int now[9];//当前状态每种宝石的数目
int dp[1<<21];//1表示还剩哪些位可以选,0表示该位已经选了,在该状态下一直选到结束的最大情况
int main()
{
	int g,b,s;
	while(cin>>g>>b>>s)//g-colornum b-bag s-least
	{
		if(g+b+s==0)
			break;
		memset(gem,0,sizeof(gem));
		for(int i=0;i<b;i++)
		{
			int x,y;
			scanf("%d",&x);
			for(int j=0;j<x;j++)//读取每个包中的宝石数
			{
				scanf("%d",&y);
				gem[i][y]++;
			}
		}
		int all=(1<<b);
		dp[0]=0;
		for(int i=1;i<all;i++)
		{
			dp[i]=FFF;
			memset(now,0,sizeof(now));
			for(int j=0;j<b;j++)
			{
				if((i&(1<<j))==0)//当前i状态中j不可选,即之前j已经选过了,统计出所有已经选过的点得到的当前剩余的宝石
				{
					for(int k=1;k<=g;k++)
					{
						now[k]=(now[k]+gem[j][k])%s;
					}
				}
			}
		/*	cout<<"i="<<i<<":"<<endl;
			for(int j=1;j<=g;j++)
				cout<<now[j]<<' ';
		*/	int cnt=0;
			for(int j=0;j<b;j++)
			{
				if((i&(1<<j))!=0)
				{
					cnt=0;
					for(int k=1;k<=g;k++)
					{
						int t=now[k]+gem[j][k];
						cnt+=t/s;
					}
					//cout<<"j="<<j<<"  cnt="<<cnt<<endl;
					if(cnt)
						dp[i]=max(dp[i],cnt+dp[i^(1<<j)]);
					else
						dp[i]=max(dp[i],-dp[i^(1<<j)]);
				}
			}
			//cout<<"i="<<i<<" dp="<<dp[i]<<endl;
		}
		cout<<dp[all-1]<<endl;
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值