HDU - 4778 Gems Fight! 记忆化搜索 + 状压

30 篇文章 0 订阅
9 篇文章 0 订阅

传送门:HDU4778

题意:有 G 种颜色的宝石,放在 B 个袋子里,两人轮流选袋子(每个袋子只能被选 1 次),每次将选出来的袋子中的宝石放到 cooker 中,cooker 可能会起反应。
反应条件是 cooker 中出现至少 S 个一样颜色的宝石,而且一旦起反应,每 S 个一样颜色的宝石就会获得 1 个魔法石。
作为奖励,每次获得魔法石的玩家可以再选一个袋子继续游戏,若未获得魔法石,则轮到另一名玩家进行游戏。
游戏目标是使自己获得的魔法石尽量多,双方都采取最优策略的情况下,问最终两个玩家的魔法石之差。

思路:袋子最大数量为21,因此我们可以状压表示袋子的选取状态,袋子的选取状态相同,两玩家所能获得的总魔法石的数量是一定的,因此我们可以先预处理出来,然后记忆化搜索进行状态转移,转移的时候每次都针对先手的可能状态进行转移就好了,因为后手的得分我们可以通过用当前状态的总得分(预处理)减去先手得分求出。

代码:

#include<bits/stdc++.h>
#define ll long long
#define pi acos(-1)
#define MAXN 100010
#define inf 0x3f3f3f3f
using namespace std;
typedef pair<int,int>P;
int G, B, S, n; 
int bag[30][30];
int score[1 << 21], dp[1 << 21];
void init()
{
	int up = 1 << B, cnt;
	int sum[30] = {0};
	for(int i = 0; i < up; i++)
	{
		for(int j = 0; j < B; j++)
		{
			if(i >> j & 1) continue;
			for(int k = 1; k <= G; k++)
			sum[k] += bag[j][k];
		}
		cnt = 0;
		for(int j = 1; j <= G; j++)
		{
			cnt += sum[j] / S;
			sum[j] = 0;
		}
		score[i] = cnt;
	}
}
int dfs(int status)//先手的状态为status所能获得的最大分数 
{
	if(dp[status] != -1) return dp[status];
	int tmp, delta, ans = 0;
	for(int i = 0; i < B; i++)
	{
		if(status >> i & 1)
		{
			tmp = status ^ (1 << i);
			delta = score[tmp] - score[status];
			if(delta) ans = max(ans, delta + dfs(tmp));
			else ans = max(ans, score[0] - score[status] - dfs(tmp)); 
		}
	}
	return dp[status] = ans;
} 
int main()
{
	int t;
	while(scanf("%d %d %d", &G, &B, &S), G + B + S)
	{
		memset(dp, -1, sizeof(dp));
		memset(bag, 0, sizeof(bag));
		memset(score, 0, sizeof(score));
		for(int i = 0; i < B; i++)
		{
			scanf("%d", &n);
			while(n--)
			{
				scanf("%d", &t);
				bag[i][t]++;
			}
		}
		init();
		int alice = dfs((1 << B) - 1);
		printf("%d\n", alice - (score[0] - alice));
	}
    return 0;
}

另一种实现方法: 点击打开链接

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值