hdu 1151 Air Raid(DAG有向无环图 最小路径覆盖+二分图最大匹配Hungarian)

Problem Description
Consider a town where all the streets are one-way and each street leads from one intersection to another. It is also known that starting from an intersection and walking through town’s streets you can never reach the same intersection i.e. the town’s streets form no cycles.

With these assumptions your task is to write a program that finds the minimum number of paratroopers that can descend on the town and visit all the intersections of this town in such a way that more than one paratrooper visits no intersection. Each paratrooper lands at an intersection and can visit other intersections following the town streets. There are no restrictions about the starting intersection for each paratrooper.
Input
Your program should read sets of data. The first line of the input file contains the number of the data sets. Each data set specifies the structure of a town and has the format:

no_of_intersections
no_of_streets
S1 E1
S2 E2

Sno_of_streets Eno_of_streets

The first line of each data set contains a positive integer no_of_intersections (greater than 0 and less or equal to 120), which is the number of intersections in the town. The second line contains a positive integer no_of_streets, which is the number of streets in the town. The next no_of_streets lines, one for each street in the town, are randomly ordered and represent the town’s streets. The line corresponding to street k (k <= no_of_streets) consists of two positive integers, separated by one blank: Sk (1 <= Sk <= no_of_intersections) - the number of the intersection that is the start of the street, and Ek (1 <= Ek <= no_of_intersections) - the number of the intersection that is the end of the street. Intersections are represented by integers from 1 to no_of_intersections.

There are no blank lines between consecutive sets of data. Input data are correct.
Output
The result of the program is on standard output. For each input data set the program prints on a single line, starting from the beginning of the line, one integer: the minimum number of paratroopers required to visit all the intersections in the town.
Sample Input

2
4
3
3 4
1 3
2 3
3
3
1 3
1 2
2 3
Sample Output
2
1

将题目意思抽象成一张图大概可以这样理解:在一张有向无环图中(DAG),找出一些路径,伞兵经过这些路径能够遍历到图上所有的节点,此外,路径的数量应该是最少的。

对于这类问题就牵扯到一些要新学的东西了,不过并不完全陌生,它还跟之前接触过的Hungarian算法算二分图的最大匹配数有关。

有向图最小路径覆盖定义:在一个有向图中找出数量最少的路径,这些路径可以经过图上所有的节点

而最小路径覆盖又分为最小不相交路径覆盖最小可相交路径覆盖

最小不相交路径覆盖定义:每一条路径经过的顶点各不相同

最小可相交路径覆盖:每一条路径经过的顶点可以相同

画图可以方便理解,如图是一张有向无环图
在这里插入图片描述
它的最小不相交路径覆盖数为3,可以分别为1->2->3,4,5(值得注意的是:一个节点也可以算作一条路径长度为0的路径)。
最小可相交路径覆盖数为2,分别为1->2->3,4->2->5。

那么,本题则要求的是DAG中的最小不相交路径覆盖数了,这需要联系到之前学习过的Hungarian算法。

对于Hungarian有一个很好的博客,放在这里 趣写算法系列之–匈牙利算法

先说结论,二分图最小路径覆盖数=二分图的结点数n-二分图的最大匹配数res

先说说二分图,如果某个图为二分图,那么它至少有两个顶点,且其所有回路的长度均为偶数任何无回路的的图均是二分图。题目中红色部分有提到,该图是无环的,所以为二分图。

一开始图中每个节点代表一条长为0的路径,共有n条,每次在二分图中找到一条匹配的边即将两条路径合并成了一条路径,造成路径数减1,所以有几条匹配的边,路径数就少几条。

对于最小路径覆盖数=二分图的结点数n-二分图的最大匹配数res,假定已经用Hungarian找到了我们建立的二分图的最大匹配数了,就拿上图来说,它的最大匹配数为2,假设情况为1、2相配,2、3相配,4、5两个节点落单,则 对于路径1->2->3来说,有3个节点,匹配数为2,则能遍历1、2、3三个节点的最小路径覆盖数为1(3-2=1),对于剩下的4和5就比较惨了,它们匹配数为0,则只能将这两个结点当作两个路径,因此能遍历4、5两个节点的最小路径覆盖数为2(2-0=0)。对于整个图的最小路径覆盖数而言,将各个分路径的结果相加即可,各分路径的答案都是n-res,则相加之后,整个图的答案就是n-res。

思想厘清之后才好写代码,联系Hungarian模板操作,直接展示代码:

#include<iostream>
#include<sstream>
#include<string>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#include<stack>
#include<queue>
#include<deque>
#include<set>
#include<unordered_set>
#include<map>
#include<unordered_map>
#include<bitset>
#include<utility>
using namespace std;
//---------------------------------------------------------
#define inf 0x3f3f3f3f
typedef double db;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> pii;
typedef vector<int> vi;
//---------------------------------------------------------
int t;
int n, m;
const int N = 120 + 10;
int h[N], ne[N], e[N], idx;
bool st[N];
int match[N];
void add(int a, int b)//添加边
{
	e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}
bool find(int u)//Hungarian求二分图最大匹配数
{
	for (int i = h[u]; i != -1; i = ne[i])
	{
		int j = e[i];
		if (!st[j])
		{
			st[j] = true;
			if (match[j] == 0 || find(match[j]))
			{
				match[j] = u;
				return true;
			}
		}
	}
	return false;
}
int main()
{
	ios_base::sync_with_stdio(false), cin.tie(0);
	cin >> t;
	while (t--)
	{
		idx = 0;
		memset(ne,0,sizeof ne);
		memset(e,0,sizeof e);
		memset(match, 0, sizeof match);
		memset(h, -1, sizeof h);
		cin >> n >> m;
		for (int i = 0; i < m; i++)
		{
			int x, y;
			cin >> x >> y;
			add(x, y);
		}
		int res = 0;//存储二分图最大匹配数
		for (int p = 1; p <= n; p++)
		{
			memset(st, false, sizeof st);
			if (find(p)) res++;
		}
		cout << n - res << endl;//运用最小路径覆盖数=二分图的结点数n-二分图的最大匹配数res得到题解
	}
	return 0;
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值