小希和Gardon在玩一个游戏:对一个N*M的棋盘,在格子里放尽量多的一些国际象棋里面的“车”,并且使得他们不能互相攻击,这当然很简单,但是Gardon限制了只有某些格子才可以放,小希还是很轻松的解决了这个问题(见下图)注意不能放车的地方不影响车的互相攻击。
所以现在Gardon想让小希来解决一个更难的问题,在保证尽量多的“车”的前提下,棋盘里有些格子是可以避开的,也就是说,不在这些格子上放车,也可以保证尽量多的“车”被放下。但是某些格子若不放子,就无法保证放尽量多的“车”,这样的格子被称做重要点。Gardon想让小希算出有多少个这样的重要点,你能解决这个问题么?
Input
输入包含多组数据,
第一行有三个数N、M、K(1<N,M<=100 1<K<=N*M),表示了棋盘的高、宽,以及可以放“车”的格子数目。接下来的K行描述了所有格子的信息:每行两个数X和Y,表示了这个格子在棋盘中的位置。
Output
对输入的每组数据,按照如下格式输出:
Board T have C important blanks for L chessmen.
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.
只能在给定的位置放置,求行和列的最大匹配。
仍然采用缩点的方法,将某一行或某一列看作一个点。这样每一个可以放棋子的位置,在每一行列都抽象为点的情况下,都可以看作两个点之间的一条边。在套用匈牙利算法就可以得到这个图的最大匹配,也就是最多可以放多少棋子。
之后再判断有哪些位置是必须的。首先在运行匈牙利之后,可以从match 数组中得到在哪些位置上放了棋子。 其次,最直观的方式,就是设置这个位置也不能放置棋子,再用匈牙利重新算改动过后的图的最大匹配是否变小,如果变小了,那么这个位置就是一个关键点;如果没有变小,那么这个点就不是关键点。
但是这种直观的方式存在大量重复计算,可以进行进一步的优化。我们可以在已知的结果上,清空两点之间的边,也就是设置这个点不能放置棋子,并且清空边的端点(u,v)的匹配,使这两个端点恢复为未匹配的状态。之后在运行完匈牙利算法的残余网络上,寻找端点u,v的增广路。如果其中一个端点能找到一条增广路,那么就意味着 在保持其他匹配不变(保持其他棋子不动),和不采用原有匹配(不在原放置棋子的位置放置)的情况下,也可以找到新的匹配(还有别的位置可以放,并且不与其他棋子发生冲突),就是说 这个点并不是一个关键点;反之,如果两个端点都找不到新的增广路,那么 在保持其他匹配不变(保持其他棋子不动),和不采用原有匹配(不在原放置棋子的位置放置)的情况下,就不是最大匹配(放在别的任何地方都会与其他棋子冲突),就是说 这个点是一个关键点。
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
#define colStar 105
#define Push(a) push_back(a)
#define rep(index,star,finish) for(register int index=star;index<finish;index++)
#define drep(index,star,finish) for(register int index=star;index>=finish;index--)
int store[105][105];
bool check[210];
bool used[210];
int match[210];
vector<int> adj[210];
bool dfs(int v);
bool judge(int v);
void show(int a[][105],int n,int m);
int main(){
ios::sync_with_stdio(false);
int t=1;
int n,m,k;
while(cin>>n>>m>>k){
//initialization
rep(i,1,n+1)
rep(j,1,m+1)
store[i][j]=false;
//input point
rep(i,0,k){
int commonA,commonB;
cin>>commonA>>commonB;
store[commonA][commonB]=true;
}
/*
//test
cout<<"store information:"<<endl;
show(store,n,m);
*/
//get adjacent graph
rep(i,1,n+1){
rep(j,1,m+1)
if(store[i][j]){
adj[i].Push(j+colStar);
adj[j+colStar].Push(i);
}
}
/*
//test
cout<<"adjacent information:"<<endl;
rep(i,0,210){
int len=adj[i].size();
if(len){
cout<<"vertex "<<i<<" :";
rep(j,0,len)
cout<<adj[i][j]<<" ";
cout<<endl;
}
}
cout<<endl;
*/
int ans=0;
memset(match,-1,sizeof(match));
//get match
rep(i,1,n+1){
if(match[i]<0){
memset(used,false,sizeof(used));
if(dfs(i))
ans++;
}
}
/*
//test
cout<<"match information:"<<endl;
rep(i,0,210)
if(match[i]>0)
cout<<"node "<<i<<" match node:"<<match[i]<<endl;
cout<<endl;
*/
int num=0;
//judge if point is important node
memset(check,false,sizeof(check));
rep(v,0,210){
if(match[v]>0 && !check[v]){
int u=match[v];
check[u]=true,check[v]=true;
adj[v].erase(find(adj[v].begin(),adj[v].end(),u));
adj[u].erase(find(adj[u].begin(),adj[u].end(),v));
match[v]=-1;
match[u]=-1;
memset(used,false,sizeof(used));
bool flag1=judge(v);
memset(used,false,sizeof(used));
bool flag2=judge(u);
// cout<<"v:"<<v<<" u:"<<u<<endl;
// cout<<"flag1:"<<(flag1? "true":"false")<<" flag2:"<<(flag2? "true":"false")<<endl;
if(!(flag1 || flag2)){
num++;
}
match[v]=u;
match[u]=v;
adj[v].Push(u);
adj[u].Push(v);
}
}
// Board 1 have 0 important blanks for 2 chessmen.
cout<<"Board "<<t<<" have "<<num<<" important blanks for "<<ans<<" chessmen."<<endl;
rep(i,0,210){
if(adj[i].size()!=0)
adj[i].erase(adj[i].begin(),adj[i].end());
}
t++;
}
return 0;
}
bool dfs(int v){
used[v]=true;
rep(i,0,adj[v].size()){
int u=adj[v][i],matched=match[u];
if(matched<0 || !used[matched] && dfs(matched)){
match[v]=u;
match[u]=v;
return true;
}
}
return false;
}
bool judge(int v){
used[v]=true;
rep(i,0,adj[v].size()){
int u=adj[v][i],matched=match[u];
if(matched<0 || !used[matched] && judge(matched))
return true;
}
return false;
}
void show(int a[][105],int n,int m){
rep(i,1,n+1){
rep(j,1,m+1){
cout<<a[i][j]<<" ";
}
cout<<endl;
}
}