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 4 1 2 1 3 2 1 2 2 3 3 4 1 2 1 3 2 1 3 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;
}

本文探讨了一种类似于中国象棋的游戏,在N*M的棋盘上放置尽可能多的车,并确定哪些位置是必须放置车的关键位置。通过二分匹配算法解决这一问题,并提供了详细的实现代码。
871

被折叠的 条评论
为什么被折叠?



