https://www.cometoj.com/contest/35/problem/C?problem_id=1498c++
這題要用到一種比較小衆的狀壓方法(沒見過的話可能一時比較難想到)。app
首先觀察題面,發現能夠把一我的有另外一我的沒有的點數都視做同一種(轉化一),而後點數之間也能夠任意轉化(轉化二),不影響結果,通過如上轉化,能夠將任意狀況轉化爲兩方各有一些相同點數的手牌,而後不一樣點數的手牌只有一種或沒有。函數
(感受說的好亂啊,舉個栗子吧...
\[ \begin{array}{} &2,3,5,6,6|1,2,5,5,7\\ \to&2,5,X,X,X|2,5,5,Y,Y\text{(轉化一)}\\ \to&1,2,X,X,X|1,2,2,Y,Y\text{(轉化二)}\\ \to&1,2,3,3,3|1,2,2,4,4\text{(轉化二)}\\ \end{array} \]
而後就是狀壓的部分,因爲兩人最多隻能有 \(8\) 張手牌。而手牌之間只有相同不相同纔有影響,因而把手牌變成 \(01\) 序列,即相鄰不一樣的用 \(01\) 相區別。spa
(感受仍是不清楚啊,繼續舉栗子吧...code
\(1,2,3,3,3 \to 10111\)get
$ 1,2,2,4,4 \to 10011$it
固然爲了表示有沒有對方沒有對牌,還需另外記一個布爾值。class
最後,任什麼時候刻不得打出上次打出的牌,因此再記一個整數表示上一次打出的手牌標號,爲了方便起見,這個標號是相對於本方而言的。而後若是由於是第一回合,或者對方打了本方沒有的牌等狀況,沒有這個限制的話,這個整數就記爲 \(8\) (由於本方手牌最多從 \(0\) 標號到 \(7\) 嘛)。test
細節仍是不少的,聽說出題人和驗題人的代碼也都很長,我仍是賽後膜改一天改出了下面這個代碼,感觸最深的就是把位操做封函數後會好寫不少。方法
#include<bits/stdc++.h> #define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i) #define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i) template<typename T,typename _T>inline bool chk_min(T &x,const _T y){return y<x?x=y,1:0;} template<typename T,typename _T>inline bool chk_max(T &x,const _T y){return x<y?x=y,1:0;} typedef long long ll; int dp[(1<<8)+5][2][(1<<8)+5][2][9]; //0:lose 1:draw 2:win int bin[(1<<8)+5]; //dp[a][b][c][d][e] int T; //a爲本方手牌 //b爲本方有沒有對方沒有的牌 inline int bit_take(int B,int l,int r) //c爲對方手牌 { //d爲對方有沒有本方沒有的牌 return B&(((1<<(r+1))-1)^((1<<l)-1)); //e爲上一次出的手牌標號(相對本方而言) } inline int bit_erase(int B,int x) { return bit_take(B,0,x-1)|(bit_take(B,x+1,bin[B])>>1); } inline int bit_reverse(int B,int l,int r) { return B^(((1<<(r+1))-1)^((1<<l)-1)); } inline int bit_swapping(int B,int l1,int r1,int r2) { int B1=bit_take(B,l1,r1),B2=bit_take(B,r1+1,r2); return (B^B1^B2)|(B1<<(r2-r1))|(B2>>(r1-l1+1)); } int get_dp(int A,bool a,int B,int b,int las) { int &res=dp[A][a][B][b][las]; if(~res)return res; else if(B==0) { if(las==8)return res=1; else return res=0; } res=0; int cnta=-1,cntb=-1; int ra[9],rb[9]; DOR(i,bin[A],0)if(i==bin[A]||((A>>i&1)!=(A>>(i+1)&1)))ra[++cnta]=i; DOR(i,bin[B],0)if(i==bin[B]||((B>>i&1)!=(B>>(i+1)&1)))rb[++cntb]=i; ra[cnta+1]=rb[cntb+1]=-1; FOR(i,0,cnta) { int l=ra[i+1]+1,r=ra[i]; if(i!=las) { int nA=bit_erase(A,r),na=a,nB=B,nb=b,nlas; if(l==r) { nA=bit_reverse(nA,0,r-1); if(a&&i==cnta)na=0,nlas=8; else if(i==cntb)nb=1,nlas=cntb; else { int x=rb[cntb+1]+1,y=rb[i+1],z=rb[i]; nB=bit_reverse(nB,x,y); if((cntb-b-i)&1)nB=bit_reverse(nB,y+1,z); nB=bit_swapping(nB,x,y,z); if(b)nlas=8; else nlas=cntb,nb=1; } } else { if(a&&i==cnta)nlas=8; else nlas=i; } chk_max(res,2-get_dp(nB,nb,nA,na,nlas)); } } return res; } void solve() { int n; int A[25]={0},B[25]={0}; scanf("%d",&n); FOR(i,1,n) { int x; scanf("%d",&x); A[x]++; } FOR(i,1,n) { int x; scanf("%d",&x); B[x]++; } int X=0,Y=0,x=0,y=0; bool cur=1; FOR(i,1,20) { if(A[i]&&!B[i])x+=A[i]; else if(!A[i]&&B[i])y+=B[i]; else if(A[i]&&B[i]) { FOR(j,1,A[i])X=(X<<1)|cur; FOR(j,1,B[i])Y=(Y<<1)|cur; cur^=1; } } FOR(i,1,x)X=(X<<1)|cur; FOR(i,1,y)Y=(Y<<1)|cur; //轉化手牌 int res=get_dp(X,x!=0,Y,y!=0,8); if(res==0)puts("dreamoon wins"); else if(res==1)puts("Draw"); else if(res==2)puts("AA wins"); } int main() { bin[1]=0;FOR(i,2,1<<8)bin[i]=bin[i>>1]+1; memset(dp,-1,sizeof(dp)); int T; scanf("%d",&T); while(T--)solve(); return 0; }