棋盘游戏 二分匹配

小希和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;
    }
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值