POJ - 1422 Air Raid (最小路径覆盖 = 总点数 - 最大匹配)

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

题意:

n*n的方阵,存在若干条有向边相连,选择若干个点组成的路为一条参观路径,所有的路径都不存在相同点,选择最少路径,使得这些路径包括所有节点,即最小路径覆盖问题

分析:

在有向无环图中,假设没有边相连,则所有节点都是一条长度为0的路劲,此时最小路径覆盖数为总结点数

若此时多出一条边1-2,则两个节点1、2为同一条路劲,此时最小路径覆盖数-1

若再多一条1-3,则最小路径覆盖数不变,因为起点与上一次相同,即两条边不是匹配边,只能选1-2或1-3

若多的第二条为2-3,则与第一条路1-2为同一条路劲,此时最小路径覆盖数-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 200
#define MIN -1e5
#define INF 0x7f7f7f7f

int t, n, m;

int x[MAX], y[MAX]; // 行、列点集合
int Edge[MAX][MAX]; // 有向无环图
int vis[MAX]; // path递归防止死循环的访问数组

int path(int u) // 设置 或 更新最大匹配的边
{
	for(int v = 1; v<=n; v++) // 便利所有y(列)集合 节点
	{
		if(vis[v] == 0 && Edge[u][v] == 1) // 递归中未访问 && 该列节点v与行节点u有变相连(有方向 u->v)
		{
			vis[v] = 1;
			if(y[v] == -1 || path(y[v])) // 该列节点v没有被行节点u匹配过 || 已经被行节点y【v】匹配过,尝试是否能够更新该匹配
			{
				x[u] = v;
				y[v] = u;
				return 1;
			}
		}
	}
	return 0;
}

void solve()
{
	int ans = 0;
	memset(x, -1, sizeof(x));
	memset(y, -1, sizeof(y));
	for(int u = 1; u<=n; u++) // 遍历所有x行节点
	{
		if(x[u] == -1) // 未被匹配过
		{
			memset(vis, 0, sizeof(vis));
			ans += path(u); // 匹配成功, 最大匹配数+1
		}
	}
	printf("%d\n", n - ans); // 最小路径覆盖 == 总节点数 - 最大匹配数
}

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

	scanf("%d", &t);
	while(t--)
	{
		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;
		}
		solve();
	}
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值