toj 3345. Chinese Chess 关键匹配

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

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

GDRetop

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值