HDU

来源:互联网 发布:pets考试有用吗 知乎 编辑:程序博客网 时间:2024/06/11 10:35

题目链接


题意:


有 G 种颜色的宝石,放在 B 个袋子里(每种颜色可以放多个)。

两人轮流选袋子(每个袋子只能被选 1 次),每次将选出来的袋子中的宝石放到 cooker 中,cooker 可能会起反应。

反应条件是 cooker 中出现 S 个一样颜色的宝石,而且一旦起反应,每 S 个一样颜色的宝石就会获得 1 个魔法石(同时反应)。

作为奖励,每次反应结束后当前玩家可以再选一个袋子继续游戏。

游戏目标是自己获得的魔法石尽量多,双方都采取最优策略的情况下,问最终两个玩家的魔法石之差。


思路:

首先我们知道两个人能拿到的魔法石的总分一定是确定的sum,并且如果一个人拿到的价值为val,另一个人拿到的魔法石数量就是 sum-val,这个我们可以预处理出来.最后的差也就是2*val-sum

然后我们发现bag的数量最多21个,并且每个袋子只能选择一次,也就是说我这个人能够得到多少魔法石只和我选择了哪些袋子有关系,

所以我们可以枚举1<<21种状态,表示在当先手面对state的局面是还能获得的最大数量是多少.这个过程可以通过记忆化搜索来实现,

dp[sta]表示先手(注意这里的先后手是相对的)面对sta状态下继续取bag能得到的最大数量是多少.

这里我们0表示bag已经被人拿走了,1表示当前bag可取。我们可以预处理出所有sta状态下已经获得的最大魔法石数量,如果当前某一位为1我们取了这个包裹,那么增加的魔法石数量即为 cha = score[new]-score[new].如果此时大于0,表示取了新bag可以获得魔法石,可以继续取.转移方程为  cha + dfs(new) 。否则让另一个人取,此时变为后手.所能获得的最大价值变为 score[0]-score[old] -dfs(new)

.score[0]-score[old] 表示当前还剩多少价值,并且减去另一个人面对new状态所获得最大数量.


#include<bits/stdc++.h>using namespace std;const int maxn = 1 << 21;int G,B,S;int dp[maxn],score[maxn],col[22][22];void init(){int s[22] = {0};for(int i = 0;i < (1 << B);++i){for(int j = 0;j < B;++j){if(i >> j & 1) continue;for(int c = 1; c <= G;++c)s[c] += col[j][c];}int ans = 0;for(int c = 1;c <= G;++c){ans += s[c]/S;s[c] = 0;}score[i] = ans;}return ;}int dfs(int sta){if(dp[sta] != -1) return dp[sta];int res = 0;for(int i = 0;i < B;++i){if(sta >> i & 1){int tmp = sta ^ (1 << i);int cha = score[tmp] - score[sta];if(cha) res = max(res,cha+dfs(tmp));else res = max(res,score[0] - score[sta] - dfs(tmp));//当前剩余的减对方取的}}//puts("1");return dp[sta] = res;}int main(){while(scanf("%d %d %d",&G,&B,&S) != EOF){if(G + B + S == 0) break;memset(dp,-1,sizeof dp);memset(col,0,sizeof col);memset(score,0,sizeof score);for(int i = 0;i < B;++i){int num ;scanf("%d",&num);while(num--){int t ;scanf("%d",&t);col[i][t]++;}}init();int ans = dfs((1 << B) - 1);cout<<2*ans-score[0]<<endl;}return 0;} 




原创粉丝点击