P1062 · OFFICIAL SOLUTION

P1062 卡牌游戏 官方题解

Gioush OJ · P1062 卡牌游戏

【题意简述】

两名玩家轮流拿走一对正面数值相同或背面数值相同的牌,无法操作者失败,判断先手是否必胜。

【正解】

用位掩码 maskmask 表示仍在牌堆中的牌。预处理所有合法牌对对应的二进制掩码。定义 win(mask)win(mask) 表示当前状态是否为必胜态,则

win(mask)=emask, e 为合法牌对¬win(maske).win(mask)=\bigvee_{e\subseteq mask,\ e\text{ 为合法牌对}}\neg win(mask\setminus e).

没有合法牌对时为必败态。记忆化搜索所有可达状态即可。

【正确性】

若存在一步走到必败态,当前玩家选择该步即可获胜;若所有合法操作都走到必胜态,对手总能获胜,当前状态必败。递推与最优策略定义完全一致。

【复杂度】

设合法牌对数为 mm,时间复杂度 O(m2n)O(n22n)\mathcal O(m2^n)\subseteq\mathcal O(n^2 2^n),空间复杂度 O(2n)\mathcal O(2^n)

【参考代码】

/*Author:EhundateghDate:2026/8/17Name:poker.cppYou steal,I kill.*/#include <vector>#include <cstdio>#include <cstring>#define MAXN 21using namespace std;int c,T,n,a[MAXN],b[MAXN];signed char Dp[1<<20];vector <int> Pair; int Calc(int Now){    if(Dp[Now]!=-1) return Dp[Now];    for(int i=0;i<(int)Pair.size();i++){        if((Now&Pair[i])==Pair[i]&&!Calc(Now^Pair[i])) return Dp[Now]=1;    }    return Dp[Now]=0;} void Solve(){    scanf("%d",&n);Pair.clear();    for(int i=1;i<=n;i++) scanf("%d%d",&a[i],&b[i]);    for(int i=1;i<=n;i++){        for(int j=i+1;j<=n;j++){            if(a[i]==a[j]||b[i]==b[j]) Pair.push_back((1<<(i-1))|(1<<(j-1)));        }    }    memset(Dp,-1,sizeof(signed char)*(1<<n));    puts(Calc((1<<n)-1)?"Ehundategh":"tfbz");} int main(){    scanf("%d%d",&c,&T);    while(T-->0) Solve();    return 0;}