匈牙利算法+最小点覆盖最佳入门题 UVA-11419

题目大意:
在给定r*c的网格中,给出敌人在网格里的位置,你有一个武器,一发炮弹可以打死一行或一列的所有敌人。求出最少的炮弹数可以把所有敌人干掉,并且输出每次发射炮弹的位置

题解:
这个题典型符合二分图最大匹配问题,以网格的行作为x,列作为y,敌人的位置作为边(如敌人位置为(1,2),则x中1与y中2应该连一条边)。接着用匈牙利算法最大匹配数M,即使用的最少炮弹数。对于要求出每次发射炮弹的位置,则需要用到最小点覆盖König定理的简单证明步骤反推回去)。具体看代码,有解释
求最小点覆盖,可参考这两幅图
这里写图片描述这里写图片描述

AC代码:

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int MAXN=1005;
bool map[MAXN][MAXN];
bool visited[MAXN];
int link[MAXN],antilink[MAXN]; //antilink记录x和谁匹配了,用于之后找增广路,便于标记
bool flagx[MAXN],flagy[MAXN];  //用于记录哪些点被标记了
int r,c,n,ans;
//经典匈牙利算法
bool dfs(int x){
    for(int i=1;i<=c;++i){
        if(map[x][i]&&!visited[i]){
            visited[i]=1;
            if(link[i]==0||dfs(link[i])){
                link[i]=x;
                antilink[x]=i;
                return true;
            }
        }
    }
    return false;
}

void search(){
    for(int i=1;i<=r;++i){
        memset(visited,0,sizeof(visited));
        if(dfs(i)) ans++;
    }
}
//求最小点覆盖是哪些点
void Minpoints(int y){
    flagy[y]=1;  //标记y侧的点
    for(int i=1;i<=r;++i){
        if(map[i][y]&&!visited[i]){
            flagx[i]=1;  //标记路上经过x侧的点
            visited[i]=1;
            if(antilink[i]==0) return; //无路可走了
            Minpoints(antilink[i]); //不断走下去
        }
    }
    return ;
}
int main(){
    while(scanf("%d%d%d",&r,&c,&n)!=EOF){
        memset(map,0,sizeof(map));
        memset(link,0,sizeof(link));
        memset(antilink,0,sizeof(antilink));
        memset(flagx,0,sizeof(flagx));
        memset(flagy,0,sizeof(flagy));
        ans=0;
        for(int i=0;i<n;++i){
            int x,y;
            scanf("%d%d",&x,&y);
            map[x][y]=1;
        }
        search();
        //从y侧每个没有被匹配的点作为起始开始找增广路
        for(int j=1;j<=c;++j){
            memset(visited,0,sizeof(visited));
            if(link[j]==0){
                Minpoints(j);
            }
        }
        cout<<ans<<" ";
        //右边没有打上记号的点,加上左边已经有记号的点组成了最小覆盖点集
        for(int j=1;j<=c;++j){
            if(flagy[j]==0)cout<<"c"<<j<<" ";
        }
        for(int i=1;i<=r;++i){
            if(flagx[i]==1)cout<<"r"<<i<<" ";
        }
        cout<<endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值