1. 程式人生 > >【LOJ】#2447. 「NOI2011」兔兔與蛋蛋的遊戲

【LOJ】#2447. 「NOI2011」兔兔與蛋蛋的遊戲

putc swap AR 取反 make pla ems name ++

題解

對於75分來說,操作肯定不會成環,可以暴搜
看成空格在移動,空格移動到原來的位置肯定經歷了偶數個格子,但是操作的人是兩個不同的人,所以肯定不會成環

對於滿分做法,要找到一種更好的方式判先手是否會勝

我們看成空格在移動,每次空格必然是走一個黑棋,走一個白棋,這顯然是一條交錯路,我們考慮二分圖

把先手看成白色,後手看成黑色,因為空格可以移動到先手所在的位置,所以空格看成黑色

在每個相鄰的黑白格子之間連一條邊

我們就相當於在這條路上找一條路,起點是空格,使得經過的的邊數是奇數,後手每個決策的點都只能走出偶數條邊

我們考慮最大匹配

如果一個點不一定在最大匹配上
那麽這個點一定會順著匹配邊走出一條偶數條邊的交錯路(這時候這偶數條邊在匹配內的狀態全部取反,這個點就不在最大匹配內了)

如果一個點一定在最大匹配上
那麽這個點一定不會走出一條偶數條邊的交錯路

如果一個點不一定在最大匹配上,那麽這個點的相鄰點一定在最大匹配上
如果這兩個點都不在某個最大匹配上,那麽這兩個點可以匹配

如果這個點在某個最大匹配上,相鄰點不在匹配中,由於這個點不一定在最大匹配上,可以走出一條偶數條邊的交錯路,加上到相鄰點的這條邊,是可以增廣的,就不是最大匹配了

那麽我們可以發現,如果起點一定在最大匹配上,那麽先手必勝,如果起點不一定在最大匹配上,它下一次移動到任意一個點都是後手必勝

所以如果空格所在的點必定在最大匹配上,先手必勝,否則,後手必勝

這就變成了跑2000次二分圖匹配的題了

代碼

#include <iostream>
#include <algorithm> #include <cstdio> #include <cstring> #include <vector> #include <set> #include <cmath> #include <bitset> #define enter putchar(‘\n‘) #define space putchar(‘ ‘) //#define ivorysi #define pb push_back #define mo 974711 #define pii pair<int,int>
#define mp make_pair #define fi first #define se second #define MAXN 1000005 using namespace std; typedef long long int64; typedef double db; template<class T> void read(T &res) { res = 0;char c = getchar();T f = 1; while(c < ‘0‘ || c > ‘9‘) { if(c == ‘-‘) f = -1; c = getchar(); } while(c >= ‘0‘ && c <= ‘9‘) { res = res * 10 - ‘0‘ + c; c = getchar(); } res = res * f; } template<class T> void out(T x) { if(x < 0) {x = -x;putchar(‘-‘);} if(x >= 10) out(x / 10); putchar(‘0‘ + x % 10); } int N,M,K,posx,posy; int ans[1005],tot; char s[45][45]; int matk[2005]; int dx[] = {0,-1,0,1},dy[] = {1,0,-1,0}; bool vis[2005]; struct node { int to,next; }E[8005]; int head[2005],sumE; void add(int u,int v) { E[++sumE].to = v; E[sumE].next = head[u]; head[u] = sumE; } int id(int x,int y) { return (x - 1) * M + y; } bool match(int x) { for(int i = head[x] ; i ; i = E[i].next) { int v = E[i].to; if(!vis[v]) { vis[v] = 1; if(!matk[v] || match(matk[v])) { matk[v] = x; return true; } } } return false; } bool check(char c) { memset(head,0,sizeof(head)); memset(matk,0,sizeof(matk)); sumE = 0; for(int i = 1 ; i <= N ; ++i) { for(int j = 1 ; j <= M ; ++j) { if(s[i][j] == c) { for(int k = 0 ; k < 4 ; ++k) { int tx = i + dx[k],ty = j + dy[k]; if(tx < 1 || tx > N || ty < 1 || ty > M) continue; if(s[tx][ty] != c) { add(id(i,j),id(tx,ty)); add(id(tx,ty),id(i,j)); } } } } } for(int i = 1 ; i <= N ; ++i) { for(int j = 1 ; j <= M ; ++j) { if(s[i][j] == c) { memset(vis,0,sizeof(vis)); match(id(i,j)); } } } if(!matk[id(posx,posy)]) return false; memset(vis,0,sizeof(vis)); vis[id(posx,posy)] = 1; if(!match(matk[id(posx,posy)])) return true; else return false; } void Solve() { read(N);read(M); for(int i = 1 ; i <= N ; ++i) { scanf("%s",s[i] + 1); for(int j = 1 ; j <= M ; ++j) { if(s[i][j] == ‘.‘) { posx = i;posy = j; } } } read(K); int x,y; for(int i = 1 ; i <= K ; ++i) { read(x);read(y); bool flag1 = check(‘O‘); swap(s[x][y],s[posx][posy]); posx = x;posy = y; bool flag2 = check(‘X‘); if(flag1 && flag2) ans[++tot] = i; read(x);read(y); swap(s[x][y],s[posx][posy]); posx = x;posy = y; } out(tot);enter; for(int i = 1 ; i <= tot ; ++i) { out(ans[i]);enter; } } int main() { #ifdef ivorysi freopen("f1.in","r",stdin); #endif Solve(); }

【LOJ】#2447. 「NOI2011」兔兔與蛋蛋的遊戲