hdu3861 The King’s Problem (强连通缩点+最小路径覆盖)

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=3861


/*题解:
state划分规则:至少存在一条路径可以从city u到city v,
也存在一条路径可以从city v到city u,则u,v必须分在一个state
(强连通子图必须分在一个state).一个state内u,v至少一方能够到达对方,
一个点只能分在一个state.(最小路径覆盖).将每个强连通图看成一个点,二分匹配。
*/
#include <cstdio>
#include <cstring>
#include <vector>
#define MAXN 5002
#define INF 0x3f3f3f3f
using namespace std;

struct node
{
	int to,next;
}edge[100002];

vector<int> mp[MAXN];
int n,m,belong[MAXN],from[MAXN],visited[MAXN];
int head[MAXN],instack[MAXN],low[MAXN],dfn[MAXN];  
int stack[MAXN],tot,Dindex,top,Bcnt;

void Init()  
{//初始化  
	tot=0,top=0,Dindex=0,Bcnt=0;  
	memset(head,-1,sizeof(head));  
	memset(instack,0,sizeof(instack));  
	memset(dfn,0,sizeof(dfn));  
	memset(low,0,sizeof(low)); 
	memset(belong,0,sizeof(belong));
	for(int i=0;i<=n;++i)
		mp[i].clear();
}  

void addEdge(int from,int to)  
{
	edge[tot].to=to;  
	edge[tot].next=head[from];  
	head[from]=tot++;  
}  

void Tarjan(int x)
{
	int i,u;
	dfn[x]=low[x]=++Dindex;//时间戳
	stack[top++]=x;
	instack[x]=1;
	for(i=head[x];i!=-1;i=edge[i].next)
	{
		u=edge[i].to;
		if(!dfn[u])
		{
			Tarjan(u);
			low[x]=low[x]>low[u]?low[u]:low[x];
		}
		else if(instack[u]&&low[x]>dfn[u])
			low[x]=dfn[u];
	}
	if(low[x]==dfn[x])
	{
		int v;
		Bcnt++;
		do 
		{
			v=stack[--top];
			instack[v]=0;
			belong[v]=Bcnt;
		} while (v!=x);
	}
}

int find(int x)//匈牙利算法
{
	int i,temp,size;
	size=mp[x].size();
	for(i=0;i<size;++i)
	{
		temp=mp[x][i];
		if(!visited[temp])
		{
			visited[temp]=1;
			if(from[temp]==-1||find(from[temp]))
			{
				from[temp]=x;
				return 1;
			}
		}
	}
	return 0;
}

int hungary()
{
	int i,sum=0;
	memset(from,-1,sizeof(from));
	for(i=1;i<=Bcnt;++i)
	{
		memset(visited,0,sizeof(visited));
		if(find(i))
			sum++;
	}
	return sum;
}

int Scan()          
{          
	char ch;          
	int ret=0;          
	while((ch=getchar())<'0'||ch>'9');          
	while(ch>='0'&&ch<='9')          
	{          
		ret=ret*10+(ch-'0');          
		ch=getchar();          
	}          
	return ret;          
} 

int main()
{
	int test,i,u,v,cnt;
	scanf("%d",&test);
	while(test--)
	{
		scanf("%d %d",&n,&m);
		Init();
		while(m--)
		{
			u=Scan();
			v=Scan();
			//scanf("%d %d",&u,&v);
			addEdge(u,v);
		}
		for(i=1;i<=n;++i)
		{
			if(!dfn[i])
				Tarjan(i);
		}
		for(u=1;u<=n;++u)
		{
			for(i=head[u];i!=-1;i=edge[i].next)
			{
				v=edge[i].to;
				if(belong[u]!=belong[v])//u,v不在一个强连通图里.
					mp[belong[u]].push_back(belong[v]);
			}
		}
		cnt=hungary();
		printf("%d\n",Bcnt-cnt);
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值