lightoj 1429 Assassin`s Creed (II) (强联通缩点+闭包+最小不相交路径覆盖)

1429 - Assassin`s Creed (II)

   PDF (English)StatisticsForum
Time Limit: 4 second(s)Memory Limit: 32 MB

Ezio needs to kill N targets located in N different cities. The cities are connected by some one way roads. As time is short, Ezio can send a massage along with the map to the assassin's bureau to send some assassins who will start visiting cities and killing the targets. An assassin can start from any city, he may visit any city multiple times even the cities that are already visited by other assassins. Now Ezio wants to find the minimum number of assassins needed to kill all the targets.

Input

Input starts with an integer T (≤ 70), denoting the number of test cases.

Each case starts with a blank line. Next line contains two integers N (1 ≤ N ≤ 1000) and M (0 ≤ M ≤ 10000), where N denotes the number of cities and M denotes the number of one way roads. Each of the next M lines contains two integers u v (1 ≤ u, v ≤ N, u ≠ v) meaning that there is a road from u to v. Assume that there can be at most one road from a city u to v.

Output

For each case, print the case number and the minimum number of assassins needed to kill all targets.

Sample Input

Output for Sample Input

3

 

5 4

1 2

1 3

4 1

5 1

 

7 0

 

8 8

1 2

2 3

3 4

4 1

1 6

6 7

7 8

8 6

Case 1: 2

Case 2: 7

Case 3: 2

Note

Dataset is huge, use faster I/O methods.

题目大意:有N个地方,每个地方有一个人,要派出忍者刺杀他们,每个忍者可以走重复的城市,问最少派出多少个忍者(有向图)

刚看到题的时候想着是最小路径覆盖,就直接写了二分图但是不对,看了题目才发现,这是有向图,有环,而且是可相交路径覆盖。那就不能单纯地写一个二分图匹配。首先要求闭包,详见最小路径覆盖问题值得注意的地方。再对这个图跑一遍tarjan求出所有的环,将其缩点,最后建新图跑一遍二分图匹配即可

#include<iostream>
#include<cstdio>
#include<cstring>
#include<stack>
#include<queue>
#include<vector>
using namespace std;
const int maxn=2010;
const int maxm=1e6+7;
struct Node
{
	int to;
	int next;
}edge[maxm];
struct Match
{
	int to;
	int next;
}match_edge[maxm];
int match_cnt;
int match_head[maxn];
int cnt;
int iindex;
int scc_cnt;
int head[maxn];
int dfn[maxn];
int low[maxn];
bool vis[maxn];
int belong[maxn];
int match[maxn];
vector<int> G[maxn];
stack<int> sta;
void init()
{
	memset(head,-1,sizeof(head));
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(belong,0,sizeof(belong));
	memset(match_head,-1,sizeof(match_head));
	while(!sta.empty()) sta.pop();
	for(int i=0;i<maxn;i++) G[i].clear();
	match_cnt=scc_cnt=cnt=iindex=0;
	return;
}
void add(int u,int v)
{
	edge[cnt].to=v;
	edge[cnt].next=head[u];
	head[u]=cnt++;
	return;
}
void addedge(int u,int v)
{
	match_edge[match_cnt].to=v;
	match_edge[match_cnt].next=match_head[u];
	match_head[u]=match_cnt++;
	return;
}
void tarjan(int node)
{
	dfn[node]=low[node]=++iindex;
	sta.push(node);
	vis[node]=true;
	for(int i=head[node];~i;i=edge[i].next)
	{
		int v=edge[i].to;
		if(!dfn[v])
		{
			tarjan(v);
			low[node]=min(low[node],low[v]);
		}
		else if(vis[v])
		{
			low[node]=min(low[node],dfn[v]);
		}
	}
	if(dfn[node]==low[node])
	{
		int v=-1;
		++scc_cnt;
		while(node!=v)
		{
			v=sta.top();
			sta.pop();
			vis[v]=false;
			belong[v]=scc_cnt;
		}
	}
	return;
}
void bfs(int node)
{
	queue<int> que;
	int len=G[node].size();
	vis[node]=true;
	for(int i=0;i<len;i++)
	{
		int v=G[node][i];
		if(vis[v]) continue;
		else
		{
			que.push(v);
			vis[v]=true;
			add(node,v);
		}
	}
	while(!que.empty())
	{
		int now=que.front();
		que.pop();
		int now_len=G[now].size();
		for(int i=0;i<now_len;i++)
		{
			int v=G[now][i];
			if(vis[v]) continue;
			else
			{
				vis[v]=true;
				que.push(v);
				add(node,v);
			}
		}
	}
	return;
}
bool dfs(int node)
{
	for(int i=match_head[node];~i;i=match_edge[i].next)
	{
		int v=match_edge[i].to;
		if(!vis[v])
		{
			vis[v]=true;
			if(match[v]==-1||dfs(match[v]))
			{
				match[v]=node;
				return true;
			}
		}
	}
	return false;
}
int main()
{
	//freopen("in.txt","r",stdin);
	//freopen("out.txt","w",stdout);
	int test;
	scanf("%d",&test);
	for(int cas=1;cas<=test;cas++)
	{
		init();
		int n,m;
		scanf("%d%d",&n,&m);
		for(int i=0;i<m;i++)
		{
			int u,v;
			scanf("%d%d",&u,&v);
			G[u].push_back(v);
		}
		for(int i=1;i<=n;i++)
		{
			memset(vis,false,sizeof(vis));
			bfs(i);
		}
		memset(vis,false,sizeof(vis));
		for(int i=1;i<=n;i++)
		{
			if(!dfn[i])
			{
				tarjan(i);
			}
		}
		for(int node=1;node<=n;node++)
		{
			for(int i=head[node];~i;i=edge[i].next)
			{
				int v=edge[i].to;
				if(belong[node]!=belong[v])
				{
					addedge(belong[node],belong[v]);
				}
			}
		}
		int ans=0;
		memset(match,-1,sizeof(match));
		for(int i=1;i<=scc_cnt;i++)
		{
			memset(vis,false,sizeof(vis));
			if(dfs(i))
			{
				ans++;
			}
		}
		printf("Case %d: %d\n",cas,scc_cnt-ans);
	}
	return 0;
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值