傳送門: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;
}
另一種實現方法:點擊打開鏈接