uva11419 SAM I AM

这题值得一做,,,路径的打印值得一学。

——>>以每行为X结点,每列为Y结点,有石头的点的横纵坐标关系为边建立二分图,那么,对于一个石头的位置,就是一条边,只要在这条边2个端点中的1个设大炮就可打掉这个石头,这正正是二分图的最小点覆盖。

对于具体的位置,则可从X中所有的未盖点出发,寻找增广路,并且标记沿途经过的结点,那么X中未被标记的点和Y中被标记的点即为所求,这是因为:

从X中的未盖点出发,终点一定在X结点(否则存在增广路,与最大匹配矛盾),那么这次增广中标记Y结点的数量就会比标记X结点的数量少1,故取已标记的Y结点用的大炮就会比取已标记的X结点的数量要少1。

对于X中未被标记的结点a,首先a是已盖点,Y中必有结点b与之匹配,且b一定不和X中的未盖点相连(否则a,b一定被标记了),若a还与Y中的未盖点相连,则应取a,而不是b及与a相连的Y中的未盖点,这样只在a一个点设大炮就可解决掉这几个石头;若a连的还有Y中的已盖点,那么那点由它的匹配点去处理,a不用去处理它;若a不与Y的其它点相连,那么取a点或者取b都行,我取了a点,所以这些情况取已盖点a就是。

综上可得取X中未被标记的点和Y中被标记的点就是设大炮的行与列。


// #pragma comment(linker, "/STACK:1024000000,1024000000")
/*
这题是最小点覆盖问题,然后确定炮弹放的位置。显然先跑遍匈牙利,
然后确定位置。跑完匈牙利后,对于左右两个集合而言,如果x集合中
的点x[i]没有匹配,就是linkx[i] == -1,那么有两种情况,第一
种是根本就不存在y集合中的点与其连接(此事就不用放),第二种是
与其连接的y集合中的点已经被其他x中的点匹配了(此事就放在y中的
这个点上面)。也就是此事就是一点对多点,现在会选择这个点;
*/
#include <iostream>
#include <algorithm>
#include <iomanip>
#include <sstream>
#include <string>
#include <stack>
#include <queue>
#include <deque>
#include <vector>
#include <map>
#include <set>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#include <limits.h>
// #define DEBUG
#ifdef DEBUG
#define debug(...) printf( __VA_ARGS__ )
#else
#define debug(...)
#endif
#define MEM(x,y) memset(x, y,sizeof x)
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> ii;
const int inf = 1 << 30;
const int INF = 0x3f3f3f3f;
const int MOD = 1e9 + 7;
const int maxn = 1010;
vector<int> G[maxn];
int linky[maxn], linkx[maxn];
int r,c,n;
int cnt;
bool usedy[maxn], usedx[maxn];
bool dfs(int u){
	usedx[u] = true;
	for (int i = 0;i < G[u].size();++i){
		int v = G[u][i];
		if (usedy[v]) continue;
		usedy[v] = true;
		if (linky[v] == -1 || dfs(linky[v])){
			linky[v] = u;
			linkx[u] = v;
			return true;
		}
	}
	return false;
}
void solve(){
	MEM(linky, -1);
	MEM(linkx, -1);
	for (int i = 0;i <= r;i++){
		if (G[i].size()){
			MEM(usedy, false);
			if (dfs(i)) cnt++;
		}
	}
	printf("%d",cnt);//需要的炮弹数目
	//确定放的位置
	MEM(usedy, false);
	MEM(usedx, false);
	/*这里就是checkx集合中的点。如果这个点被匹配了,先不管
	然后就是check未匹配的点,不管有无其他点与其连接,这里都
	显然不用放炮弹。*/
	for (int i = 1;i <= r;i++)
		if (linkx[i] == -1) dfs(i);
	for (int i = 1;i <= r;i++)
		if (!usedx[i])
			printf(" r%d",i);
	for (int i = 1;i <= c;++i)
		if (usedy[i]) printf(" c%d",i);
	puts("");
}
int main()
{	
	// freopen("in.txt","r",stdin);
	// freopen("out.txt","w",stdout);
	while(scanf("%d%d%d",&r,&c,&n) && (r+c+n)){
		//Init();
		for(int i=0;i<=r;i++)
			G[i].clear();
		cnt = 0;
		int a,b;
		while(n--){
			scanf("%d%d",&a,&b);
			G[a].push_back(b);
		}
		solve();
	}
	return 0;
}


你可能感兴趣的:(uva,最小点覆盖)