传送门: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;
}
另一种实现方法: 点击打开链接