POJ - 3041 Asteroids (二部图最大匹配 = 最小点覆盖)

题目:http://poj.org/problem?id=3041

题意:

n*n矩阵,k组数据,每组u,v表示,矩阵【u,v】有障碍物,每次子弹操作可以清楚一行或一列的障碍物,求最少操作数

分析:

把行列划分成两个点集,x集合(行)、y集合(列),把有向图G矩阵转化成两个点集的二部图,则【u,v】表示为x【u】 = v, y【v】 = u

当把x【1】去掉的时候,则表示点1能到的点都被去掉了。可以理解为,一个点覆盖其他点

所以,只要求用最少的点覆盖所有的点即可 == 最小点覆盖

有定理可得: 最小点覆盖 = 最大匹配->求最大匹配即可

(*)最小点覆盖:

每一条边上至少有一个点被选中,即一个点可以覆盖若干条边,选择最少的点,使得所有边都被覆盖,则点数为最小点覆盖数

代码:

#include <stdio.h>
#include <iostream>
#include <string.h>
#include <string>
#include <math.h>
#include <algorithm>
#include <queue>
#include <stack>
#include <vector>
#include <map>

using namespace std;

#define MAX 500+10
#define MIN -1e9
#define INF 0x7f7f7f7f

int t, n, m;

int x[MAX], y[MAX]; // 点集合
int Edge[MAX][MAX]; // 有向图
int vis[MAX]; // 递归函数中防止重复访问的数组

int path(int u) // 试图匹配,包含更新匹配
{
	for(int v = 1; v<=n; v++) // 遍历y
	{
		if(vis[v] == 0 && Edge[u][v] == 1) // 当前DFS未访问过该y节点 && 能够相连
		{
			vis[v] = 1;
			if(y[v] == -1 || path(y[v])) // 当前y未连接过x || 已连接,但是可以将其更新为其他节点,把该节点空出给当前y
			{
				x[u] = v;
				y[v] = u;
				return 1; // 匹配成功
			}
		}
	}
	return 0; // 匹配失败
}

void slove()
{
	memset(x, -1, sizeof(x));
	memset(y, -1, sizeof(y));
	int sum = 0; // 成功匹配数
	for(int u = 1; u<=n; u++)
	{
		if(x[u] == -1)  // 未匹配过的行, 有机会更新匹配 
		{
			memset(vis, 0, sizeof(vis)); // 每次查找路径前,y的访问清空(每个x均可访问所有y)
			if(path(u) == 1) // 匹配成功
				sum ++ ;
		}
	}
	printf("%d\n", sum);
}

int main()
{
    int i, j;
    //freopen("a.txt", "r", stdin);

	while(~scanf("%d%d", &n, &m))
	{
		int u, v;
		memset(Edge, 0, sizeof(Edge));
		for(i = 0; i<m; i++)
		{
			scanf("%d%d", &u, &v);
			Edge[u][v] = 1;
		}
		slove();
	}
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值