HDU - 4778 Gems Fight! 記憶化搜索 + 狀壓

傳送門: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;
}

另一種實現方法:點擊打開鏈接
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章