hdu 5724 Chess (SG函式)
阿新 • • 發佈:2018-11-05
題目連結:hdu 5724
題意:有一個n行20列的棋盤,棋盤上分佈著一些棋子,A、B兩人輪流下棋,A先手,每次操作可以將某個棋子放到自己右邊的第一個空位(也就是說右邊如果已經有子,可以跳過它,沒有就右移一步),但最多20列,絕對不能超過棋盤,無棋可走的輸。
題解:進行狀態壓縮,bit來表示在一行中一個點有沒有棋子,有棋子為1,沒有棋子為0,0到(2^20-1)就代表全了所有的可能。
我們還知道1是必敗點SG[1]=0。可以拿來驗證。
程式碼:
///SG函式模板 ///類似於NIM遊戲,異或和為0為平衡狀態,異或和不為0位不平衡狀態 ///先手的在不平衡狀態下能贏, ///SG值的意義。當g(x)=k時,表明對於任意一個0<=i<k,都存在x的一個後繼y滿足g(y)=i。 ///也 就是說,當某枚棋子的SG值是k時,我們可以把它變成0、變成1、……、變成k-1 ///對於n個棋子,設它們對應的頂點的SG值分別為(a1,a2,…,an), ///再設局面(a1,a2,…,an)時的Nim遊戲的一種必勝策略是把ai 變成k, ///那麼原遊戲的一種必勝策略就是把第i枚棋子移動到一個SG值為k的頂點。 #include<cstdio> #include<algorithm> #include<cstring> using namespace std; int SG[(1<<20)+100],book[30]; void get_SG() { for(int i=0;i<(1<<20);i++) { memset(book,-1,sizeof(book)); int last=-1; for(int j=0;j<20;j++) { if(!((i>>j)&1)) ///空格在最右的位置 last=j; if((i>>j)&1) ///最右棋子的位置 { if(last!=-1) book[SG[(i^(1<<j))^(1<<last)]]=true; ///後繼狀態標記 ///找到最右邊的棋子以及可移動的空格,然後互換成後繼並標記,互換後一定比互換前的值小,因為我們是先找空格再找棋子的 /// } } int item=0; while(book[item]!=-1) item++; ///找出最小的不屬於這個集合的非負整數 // printf("item=%d\n",item); SG[i]=item; } } int main() { memset(SG,0,sizeof(SG)); get_SG(); int ncase; scanf("%d",&ncase); while(ncase--) { int n,m,ans=0,item; scanf("%d",&n); for(int i=1;i<=n;i++) { scanf("%d",&m); item=0; for(int j=1;j<=m;j++){ int x; scanf("%d",&x); item^=1<<(20-x); } // printf("%d\n",SG[item]); ans^=SG[item]; } // printf("ans=%d\n",ans); if(ans) printf("YES\n"); else printf("NO\n"); } return 0; }