題目描述
Description
Input
輸入的第一行包含兩個正整數 n、m。 接下來 n行描述初始棋盤。其中第i 行包含 m個字符,每個字符都是大寫英文字母"X"、大寫英文字母"O"或點號"."之一,分別表示對應的棋盤格中有黑色棋子、有白色棋子和沒有棋子。其中點號"."恰好出現一次。 接下來一行包含一個整數 k(1≤k≤1000) ,表示兔兔和蛋蛋各進行了k次操作。 接下來 2k行描述一局遊戲的過程。其中第 2i – 1行是兔兔的第 i 次操作(編號爲i的操作) ,第2i行是蛋蛋的第i次操作。每個操作使用兩個整數x,y來描述,表示將第x行第y列中的棋子移進空格中。 輸入保證整個棋盤中只有一個格子沒有棋子, 遊戲過程中兔兔和蛋蛋的每個操作都是合法的,且最後蛋蛋獲勝。
Output
輸出文件的第一行包含一個整數r,表示兔兔犯錯誤的總次數。 接下來r 行按遞增的順序給出兔兔“犯錯誤”的操作編號。其中第 i 行包含一個整數ai表示兔兔第i 個犯錯誤的操作是他在遊戲中的第 ai次操作。 1 ≤n≤ 40, 1 ≤m≤ 40
Sample Input
樣例一:
1 6
XO.OXO
1
1 2
1 1
樣例二:
3 3
XOX
O.O
XOX
4
2 3
1 3
1 2
1 1
2 1
3 1
3 2
3 3
樣例三:
4 4
OOXX
OXXO
OO.O
XXXO
2
3 2
2 2
1 2
1 3
Sample Output
1
1
樣例二:
0
樣例三:
2
1
2
樣例1對應圖一中的遊戲過程
樣例2對應圖三中的遊戲過程
HINT
分析
75分做法
我們把操作看做是空格在移動。
我們做這道題最先想到的就是,操作是否可能成環?
答案是否定的。我們假設環長爲
既然不能成環,而且數據還這麼小,似乎搜索很好寫。敲一發,75分到手。
滿分做法
我們發現,移動路徑上,相鄰兩個點的顏色總是不一樣的,這然我們想到了二分圖。
我們在相鄰而且顏色不相同的兩個點之間連邊,由於空格能夠走到白色格子,我們不妨把空格看做黑色。
先手在當前點能夠獲勝的條件是從當前點出發,能夠找到一條路徑長度是奇數而且先手一定能夠使路徑長度爲奇數。
由於是二分圖,那我們求個最大匹配試試。
我們發現,匈牙利算法增廣時的交錯軌不就是一條長度爲奇數的路徑嗎。
那麼我們來思考一下兩種情況。
如果當前點不一定最大匹配上,那麼它的鄰接點一定在最大匹配上。 我們假設它的鄰接點不一定在最大匹配上:
- 存在鄰接點不在時,當前點也不在。那麼久可以增廣了,和假設是最大匹配不符。
- 如果鄰接點和當前點一定有一個在,那麼鄰接點和當前點一定還通過一個有偶數條邊的交錯軌相連,加上這條邊就是一個奇環了,不是二分圖。
如果當前點一定在最大匹配上,則一定獲勝
首先,當前點一定在一條增廣路徑上,即在一條交錯軌上。這條交錯軌一定是奇數的,而且被這個點分成了一邊是奇數,一邊是偶數。但是奇數那一邊也有可能有一條偶數的交錯軌,即可能有岔路。
我們來分析一下,岔路可能有兩種情況。
顯然,這種情況你自己不去走那邊就好了。- 出現在別人的節點
如果朝奇數條交錯軌的方向走,自己的節點到別人的節點這條邊應該在最大匹配中(圖中藍色的邊),但是這條邊顯然可以被橙色的邊代替,和當前點一定在最大匹配上的假設不符。
所以,最終一定走的是奇數條邊,使後手無法行動。 也可以這麼看,先手一定可以有邊可走(沿着匹配邊走),但是後手不一定有邊可走。
- 出現在別人的節點
由此我們得出結論,如果先手所在的點在一定在最大匹配上,則先手有必勝策略,否則,後手有必勝策略。
每次操作時,我們就檢查當前空格所在位置是不是一定在最大匹配上,然後將這個位置刪掉,將空格的位置設置爲這次操作的位置。
那麼我們怎麼看當前點是不是一定在最大匹配上呢?
- 如果當前點本來就沒有匹配的點,顯然不在。
- 如果當前點所匹配的點在刪去當前點後,能夠繼續增廣,就說明當前點不一定在最大匹配上,否則一定在。
代碼
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
using namespace std;
#define MAXN 40
#define MAXM 40
#define MAXK 1000
queue<int>q;
int n,m,cx[MAXN*MAXM+10],dir[4][2]={{1,0},{-1,0},{0,1},{0,-1}},bx,by,cnt,ans[MAXK+10],k;
char s[MAXN+10][MAXN+10];
bool vis[MAXN*MAXM+10],ban[MAXN*MAXM+10];
struct node{
int v;
node *next;
}*adj[MAXN*MAXN+10],edge[MAXN*MAXN*4+10],*ecnt=edge;
inline void addedge(int u,int v){
node *p=++ecnt;
p->v=v;
p->next=adj[u];
adj[u]=p;
}
void Read(int &x){
static char c;
while(c=getchar(),c!=EOF)
if(c>='0'&&c<='9'){
x=c-'0';
while(c=getchar(),c>='0'&&c<='9')
x=x*10+c-'0';
ungetc(c,stdin);
return;
}
}
void read(){
Read(n),Read(m);
int i,j;
for(i=1;i<=n;i++)
scanf("%s",s[i]+1);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++)
if(s[i][j]=='O')
s[i][j]=0;
else if(s[i][j]=='X')
s[i][j]=1;
else
s[i][j]=1,bx=i,by=j;
}
inline int Get_id(int i,int j){
return (i-1)*m+j;
}
void bfs(){
q.push(Get_id(bx,by));
int u,x,y,tx,ty,v,d;
while(!q.empty()){
u=q.front();
q.pop();
x=(u+m-1)/m;
y=u-(x-1)*m;
for(d=0;d<4;d++){
tx=x+dir[d][0];
ty=y+dir[d][1];
if(tx&&ty&&tx<=n&&ty<=m&&s[x][y]^s[tx][ty]){
v=Get_id(tx,ty);
addedge(u,v);
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
}
void print(){
printf("%d\n",cnt);
int i;
for(i=1;i<=cnt;i++)
printf("%d\n",ans[i]);
}
bool dfs(int u){
for(node *p=adj[u];p;p=p->next){
if(!vis[p->v]&&!ban[p->v]){
vis[p->v]=1;
if(!cx[p->v]||dfs(cx[p->v])){
cx[p->v]=u;
cx[u]=p->v;
return 1;
}
}
}
return 0;
}
void solve(){
int i,u,mt;
for(i=n*m;i;i--){
if(!cx[i]){
memset(vis,0,sizeof vis);
dfs(i);
}
}
bool r1,r2;
Read(k);
for(i=1;i<=k;i++){
u=Get_id(bx,by);
ban[u]=1;
if(cx[u]){
mt=cx[u];
cx[u]=cx[mt]=0;
memset(vis,0,sizeof vis);
r1=!dfs(mt);
}
else
r1=0;
Read(bx),Read(by);
u=Get_id(bx,by);
ban[u]=1;
if(cx[u]){
mt=cx[u];
cx[u]=cx[mt]=0;
memset(vis,0,sizeof vis);
r2=!dfs(mt);
}
else
r2=0;
if(r1&&r2)
ans[++cnt]=i;
Read(bx),Read(by);
}
}
int main()
{
read();
bfs();
solve();
print();
}