The King’s Problem - targin缩点加最小路径覆盖

The King’s Problem

在一个王国里面, 国王有一个新的问题. 皇城中有N个城市M条单行路,为了让他的王国更加高效,国王想要将他的王国划分成几个州,每个城市必须属于一个州。对于两个城市(u,v),必须满足以下3个条件:

  1、如果有一条从u到v的路,也有一条从v到u的路,那么u、v必须属于同一个州;

  2、对于每一个州里的任何两个城市u、v,至少要有一方能到达另一方(必须经过同一个州的点到达)。

  3、一个城市只能属于一个州。

现在国王想要知道他的王国最少可以划分成多少个州。

Input

第一行是一个数字T,代表测试组数,接下来是T组测试。

每组测试数据的第一行包含两个整数n、m(0 < n <= 5000,0 <= m <= 100000),分别指n个城市,m条单行路,接下来m行,每行包含两个数字a、b,表示有一条从城市a到b的单行路。

Output

输出包含T行。

每组测试数据输出一行。

Sample Input

1
3 2
1 2
1 3 

Sample Output

2 

题意在寂静的Kingdom,国王遇到了一个新问题。王国里有N个城市,城市之间有许多方向性的道路。这意味着,如果有一条从U到V的道路,你只能从城市到第五城,但不能从第五城到美国城市,为了更有效地统治他的王国,国王想要把他的王国分割成几个州,每个城市必须属于一个州。更重要的是,对于每一对城市(U,V),如果有一种从u到V,从V到u,(u,v)必须属于同一状态的方法。国王必须保证,在每一个州,我们都可以从U到v,或者在每一对城市(u,V)之间从v到u,而不经过任何属于另一个州的城市。
现在国王请求你的帮助,他想知道他必须把王国分成多少个州。

思路:targin缩点+匈牙利最小路径覆盖

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define ll long long
using namespace std;
const int maxn = 5009;
int head[100001], dfn[maxn], low[maxn];//dfn是时间戳low是最小的时间戳head头
int chudu[maxn], rudu[maxn], id[maxn], all[maxn];//id是染色后的点 all是染色的点数 
bool instal[maxn];int cnt, tot, gg;//instal是判断在不在栈中
stack<int>s;
struct Edge{
	int next, to;
}edge[100009];//链式前向星存图
inline void add(int x, int y) {
	cnt++;
	edge[cnt].to = y;
	edge[cnt].next = head[x];
	head[x] = cnt;	
}
void init() {
	while(!s.empty()) {
		s.pop();
	}
	gg = 0;cnt = 0, tot = 0;
	memset(head, 0, sizeof(head));
	memset(dfn, 0, sizeof(dfn));
	memset(instal, false, sizeof(instal));
	memset(all, 0, sizeof(all));
	memset(chudu, 0, sizeof(chudu));
	memset(id, 0, sizeof(id));
	memset(edge, 0, sizeof(edge));
}
//链式前向星加边
void targin(int x) {
	dfn[x] = low[x] = ++cnt;
	s.push(x);
	instal[x] = true;
	for(int i = head[x];i;i = edge[i].next) {
		int u = edge[i].to;
		if(!dfn[u]) {
			targin(u);
			low[x] = min(low[x], low[u]);
		}
		else if(instal[u]) low[x] = min(low[x], dfn[u]);
	}
	int k;
	if(low[x] == dfn[x]) {
		++gg;
		do{
			k = s.top();s.pop();
			instal[k] = false;
			id[k] = gg;all[gg]++;//id是染色gg是第一次的点把所有的一个联通快的点缩成这个点all是存的缩点的点的数量
		}while(x != k);
	}
}//强连通
struct hunge{
	vector<int>line[maxn];int used[maxn],nxt[maxn];
	int n;
	void initma(int n) {
		this->n = n;
		for(int i = 0;i <= n;++i) {
			line[i].clear();
		}
		memset(nxt, -1, sizeof(nxt));
	}
	void add(int x, int y) {
		line[x].push_back(y);
	}
	bool find(int x)
	{
		for(int i = 0;i < line[x].size();++i)
		{
			if(line[x][i] && !used[line[x][i]])
			{
				int h = line[x][i];
				used[h] = 1;
				if(nxt[h] == -1 || find(nxt[h]))//如果这个人没匹配或这个人可以和别人匹配就让匹配
				{
					nxt[h] = x;
					return true;
				}
			}
		}
		return false;
	}//查询匹配的人
	
	int match()
	{
		int sum = 0;
		for(int i = 1;i <= n;++i)
		{
			memset(used,0,sizeof(used));
			if(find(i))
			{
				sum++;
			}
		}
		return sum;
	}//找匹配个数
		
}gao;//二分匹配板子

int main() {
	int t;
	scanf("%d", &t);
	while(t--) {
		init();
		int n, m;
		scanf("%d%d", &n, &m);
		for(register int i = 1;i <= m;++i) {
			int x, y;
			scanf("%d%d", &x, &y);
			add(x, y);
		}
		for(register int j = 1;j <= n;++j) {
			if(!dfn[j]) {
				targin(j);
			}
		} 
		gao.initma(n);
		for(register int i = 1;i <= n;++i) {
 			for(register int j = head[i];j ;j = edge[j].next) {
				int u = edge[j].to;
				if(id[i] != id[u]) {
					gao.add(id[i], id[u]);
				}
			}
		}
		
		int ans = gao.match();
		printf("%d\n", gg-ans);//gg是缩点后的点数量
	}
	return 0;
}
发布了219 篇原创文章 · 获赞 43 · 访问量 10万+

猜你喜欢

转载自blog.csdn.net/qq_43568078/article/details/103454944
今日推荐