UVA - 11419 (二分图匹配 ----最小点覆盖+ 输出覆盖的点)

UVA - 11419 (二分图匹配 ----最小点覆盖+ 输出覆盖的点)

The world is in great danger!! Mental’s forces have returned to
Earth to eradicate humankind. Our last hope to stop this great
evil is Sam “Serious” Stone. Equipped with various powerful
weapons, Serious Sam starts his mission to destroy the forces of
evil.
After fighting two days and three nights, Sam is now in front of
the temple KOPTOS where Mental’s general Ugh Zan III is waiting
for him. But this time, he has a serious problem. He is in shortage
of ammo and a lot of enemies crawling inside the temple waiting
for him. After rounding the temple Sam finds that the temple is
in rectangle shape and he has the locations of all enemies in the
temple.
All of a sudden he realizes that he can kill the enemies without entering the temple using the great
cannon ball which spits out a gigantic ball bigger than him killing anything it runs into and keeps on
rolling until it finally explodes. But the cannonball can only shoot horizontally or vertically and all the
enemies along the path of that cannon ball will be killed.
Now he wants to save as many cannon balls as possible for fighting with Mental. So, he wants to
know the minimum number of cannon balls and the positions from which he can shoot the cannonballs
to eliminate all enemies from outside that temple.
Input
The input file contains several test cases.
Here, the temple is defined as a R × C grid. The first line of each test case contains 3 integers:
R (0 < R < 1001), C (0 < C < 1001) representing the grid of temple (R means number of row and
C means number of column of the grid) and the number of enemies N (0 < N < 1000001) inside the
temple. After that there are N lines each of which contains 2 integers representing the position of
the enemies in that temple. Each test case is followed by a new line (except the last one). Input is
terminated when R = C = N = 0.
Output
For each test case there will be one line output. First print the minimum number (m) of cannonballs
needed to wipe out the enemies followed by a single space and then m positions from which he can
shoot those cannonballs. For shooting horizontally print ‘r’ followed by the row number and for vertical
shooting print ‘c’ followed by the column number. If there is more than one solution any one will do.
Sample Input
4 4 3
1 1
1 4
3 2
4 4 2
1 1
2 2
0 0 0
Sample Output
2 r1 r3
2 r1 r2

题目大意:

有一个n*m的矩阵,里面有k个小怪兽。每个小怪兽的位置也给出。
每一次可以打一枪消灭一行的怪物,或者消灭一列的怪物。问最小打多少枪。输出每一次打枪打的是 哪一行 或者 哪一列。

解题思路:

如果仅仅问打多少枪那就很简单了。那我们找的就是最少需要多少点能覆盖这些x y
我们把x y当做两个集合,进行二分图匹配,最大匹配就是最小点覆盖。
把每个小怪物的x 和 y连边,直接进行匈牙利算法就能求出最少开多少枪。

剩下的问题也是最关键的就是如何输出怎么打枪
(我比较菜,看了好多博客才看懂这个)

在这里插入图片描述
看这个图 我们怎么打枪呢? 显然是 打 x1 和 y3

用boy[i] 表示 i这个x 对应的y 什么 , gril[i] 表示i这个y 对应的x是什么

在匈牙利算法中,我们遍历每一个x,为每一个x找y

	for(int i=1;i<=n;i++){
		memset(used,0,sizeof(used));
		if(find(i))
			res++;
	}

在为x 找y的过程中,我们遍历每一个y 看能不能和x匹配上

bool find(int x){
	s[x] = 1;
	for(int i=1;i<=m;i++){
		if(mp[x][i]==1&&used[i]==0){
			used[i] = 1;
			if(girl[i]==0||find(girl[i])){
				girl[i] = x;
				boy[x] = i;
				return true;
			}
		}
	}
	return false;
}

匹配上 那就更新boy 和 girl 数组

对于我们上面我们跑一边后
boy[1] = 1 boy[2] = 3
girl[1] =1 girl[3] =2
used[1] = used[3] = 1
s[1] = s[2] = s[3] = 1;
这时候我们吧used 清空
然后再遍历一遍x 看之前没匹配上的点,就是3了 ,给她匹配y 这就说明这个y 至少连了两个点,这个y,就是我们需要的点 我们把used[y] = 1;表示第二次

遍历一遍x 找第二次没跑过的点,也就是第一次匹配上的
遍历一遍y , 只要uesd=1的点,表示第二次,匹配上的点。

写的我自己都有点看不懂了 emmmmmm。。
可以对着代码 对着上面那个图 模拟一遍 就能清楚一点。

AC代码:

#include <bits/stdc++.h>
using namespace std;
int n,m,k;
const int maxn = 1001;
int mp[maxn][maxn];
int boy[maxn];
int girl[maxn];
int used[maxn];
int s[maxn];
bool find(int x){
	s[x] = 1;
	for(int i=1;i<=m;i++){
		if(mp[x][i]==1&&used[i]==0){
			used[i] = 1;
			if(girl[i]==0||find(girl[i])){
				girl[i] = x;
				boy[x] = i;
				return true;
			}
		}
	}
	return false;
}
void solve(){
	int res = 0;
	memset(s,0,sizeof(s));
	memset(boy,0,sizeof(boy));
	memset(girl,0,sizeof(girl));
	for(int i=1;i<=n;i++){
		memset(used,0,sizeof(used));
		if(find(i))
			res++;
	}
	cout<<res<<" ";
	memset(s,0,sizeof(s));
	memset(used,0,sizeof(used));
	for(int i=1;i<=n;i++){
		if(!boy[i]) find(i);
	}
	for(int i=1;i<=n;i++){
		if(!s[i]) printf("r%d ",i);
	}
	for(int i=1;i<=m;i++){
		if(used[i]) printf("c%d ",i);
	}
	cout<<endl;
	return ;
}
void init(){
	memset(mp,0,sizeof(mp));
	memset(used,0,sizeof(used));
}
int main()
{
	while(~scanf("%d%d%d",&n,&m,&k)){
		if(n==0&&m==0&&k==0) break;
		init(); 
		for(int i=1;i<=k;i++){
			int xx,yy;
			cin>>xx>>yy;
			mp[xx][yy] = 1;
		}
		solve();
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值