3345. Chinese Chess
Time Limit: 20.0 Seconds Memory Limit: 65536K
Total Runs: 426 Accepted Runs: 47
Winnie is very interested in chinese chess. Now, let's consider a game which is similar to it. There is a
N *
M chess board, we hope we can put rooks as more as possible. But just a certain number of
postions can be put rooks on. One rook can attack another if they are in the same row or column. Now you may think this is a very simple problem for you. But as very whuacmers know, winnie is evil enough to cheat you. Let's consider some positions called critical
postions. If we don't put rook on the critical position, the maximum number of rook we can put on this chess board will reduce. How many critical positions on the chess board?
Input
Input will contain multiple test cases. The first line contains three numbers N, M, K(N, M ≤ 10000, K ≤ 100000) which indicate height, width and number of positions which can be put rook on. Then next K lines
follow, each contains two integer X ans Y which indicate we can put rook on the Xth row, Yth column.
Output
Output as follow:
Board T have C important blanks for L chessmen.
C indicate the number of critical positions ans L indicate the maximum rooks can be put.
Sample Input
3 3 41 21 32 12 23 3 41 21 32 13 2
Sample Output
Board 1 have 0 important blanks for 2 chessmen.Board 2 have 3 important blanks for 3 chessmen.
Hint: huge input, use scanf please.
題意:棋盤上有很多點,問最少設定幾個棋子可以把這些點都吃掉,還問哪些棋子放置的位置是固定的,不然有些點就不能被吃掉
關鍵匹配:
先找到最大匹配。
然後對每個匹配邊的兩個端點u,v查看是否對應唯一的匹配邊,
不最佳化會逾時的。看了別人的代碼才發現可以這樣最佳化。太神奇了
其中有兩處最佳化
#include<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<vector>using namespace std;const int maxn = 10009;int cover[maxn],pre[maxn],son[maxn],flag[maxn];vector<int>head1[maxn],head2[maxn];int nodeu,nodev;int findx(int u){//尋找左側點的增廣路 for(int i = 0;i < head1[u].size();i++){ int v = head1[u][i]; if(cover[v] == 1)continue; if(nodeu == u && nodev == v)continue; cover[v] = 1; if(pre[v] == -1 || findx(pre[v]) == 1){ pre[v] = u; son[u] = v; return 1; } } return 0;}int N,M;int begin(){//二分匹配 int res = 0; memset(pre,-1,sizeof(pre)); memset(son,-1,sizeof(son)); for(int i = 1; i <= N; i++){//最佳化,枚舉每條邊,查看是否可以找到匹配邊 if(son[i] != -1) continue; for(int j = 0;j < head1[i].size() && son[i] == -1;j++){ int v = head1[i][j]; if(pre[v] == -1){ pre[v] = i; son[i] = v;res++; } } } for(int i = 1;i <= N; i++){//尋找增廣路,增加匹配邊 if(son[i] != -1) continue; memset(cover,0,sizeof(cover)); if(findx(i) == 1) res++; } return res;}int findy(int v){//尋找右側點的增廣路 for(int i = 0;i < head2[v].size();i++){ int u = head2[v][i]; if(nodeu == u && nodev == v) continue; if(cover[u] == 1) continue; cover[u] = 1; if(son[u] == -1 || findy(son[u]) == 1){ son[u] = v; pre[v] = u; return 1; } } return 0;}int work(){ int ans = 0; int flag2 = 0; for(int i = 1;i <= N; i++) flag[i] = son[i]; for(int i = 1;i <= N; i++){ if(flag[i] == -1) continue; nodeu = i, nodev = son[i]; son[nodeu] = -1; pre[nodev] = -1; flag2 = 0;//查看一條邊的兩個端點是否都沒有增廣路 //沒有說明這條是關鍵匹配 memset(cover,0,sizeof(cover)); if(findx(nodeu) == 1) flag2 = 1; else{ memset(cover,0,sizeof(cover)); if(findy(nodev) == 1) flag2 = 1; } if(flag2 == 0){son[nodeu] = nodev ; pre[nodev] = nodeu; ans++;} else for(int j = i; j <= N; j++)//核心最佳化,可以沒有else直接最佳化 if(son[j] != flag[j]) flag[j] = -1; //如果在尋找增廣路時,匹配邊上的點發生變化,說明表示關鍵匹配, //下次無需再進行判斷了 } return ans;}int main(){ int u,v,k,t=1; while(scanf("%d%d%d",&N,&M,&k) != EOF){ for(int i = 1;i <= N; i++) head1[i].clear(); for(int i = 1;i <= M; i++) head2[i].clear(); for(int i = 0;i < k ;i++){ scanf("%d%d",&u,&v); head1[u].push_back(v); head2[v].push_back(u); } nodeu = -1,nodev = -1; int res = begin(); int ans = work(); printf("Board %d have %d important blanks for %d chessmen.\n",t++,ans,res); } return 0;}