toj 3345. Chinese Chess 关键匹配

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  NMK( NM ≤ 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;
}



你可能感兴趣的:(关键匹配)