POJ-1904 King's Quest 强连通分量求完美匹配

http://poj.org/problem?id=1904

题目意思:有n个女生和n个男生,给定一些关系表示男生喜欢女生(即两个人可以结婚),再给定一个初始匹配,表示这个男生和哪个女生结婚,初始匹配必定是合法的.求每个男生可以和哪几个女生可以结婚其他人还难能都找个女生结婚。
思路:第一反应还以为是求二分完美匹配,百度题解再知道用连通分量 0.0  将男生从1到n编号,女生从(n+1)到2*n编号,输入时如果男生u可以和女生v结婚,那么就做一条从u到v的边(u,v),对于输入的初始匹配(u,v)(表示男生u和女生v结婚),那么从v做一条到u的边(v,u),然后求解图的强连通分量,如果男身i和女生j在同一个强连通分量内,且i到j有直接的边。则他们可以结婚。



#include<stdio.h>
#include<string.h>
#include<vector>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn = 4105;
const int inf = 1<<29;
int n;
int times,ans,top;
bool vis[maxn];
int low[maxn],dfn[maxn],stack[maxn],p[maxn];
vector<int>map[maxn];
vector<int>cnt;

void dfs(int u)
{
	int v;
    dfn[u] = low[u] = ++times;                     //为节点u设定次序
    vis[u] = true;                             //判断是否在栈中
    stack[++top] = u;							  //将节点i压入栈中
	for (  int i = 0; i < map[u].size(); i ++ )						 //遍历每一条边
    {
        v = map[u][i];
		if ( !dfn[v] )					//如果节点i未被访问过
		{
			dfs(v);					 //继续向下查找
			low[u] = low[v] <= low[u]?low[v]:low[u];
		}
		else if ( vis[v] ) //如果节点i在栈中
			low[u] = dfn[v] <= low[u]?dfn[v]:low[u];
    }
    if ( dfn[u] == low[u] )     //如果节点i是强连通分量的根
    {
        ans++;                          //强连通分量数
        do
        {
            v = stack[top--];             //把栈里v上面的顶点弹栈
            p[v] = ans;
			vis[v] = false;
        }while ( v != u );
        
    }
}
void tarjan()
{
    ans = times = top = 0;                //初始化
	memset( vis,0,sizeof(vis) );
    memset( dfn,0,sizeof(dfn) );
    for( int i = 1; i <= n; i ++ )         //遍历每个节点
        if ( !dfn[i] )
            dfs(i);
}
int main()
{
    //freopen("data.txt","r",stdin);
	int m,temp,g;
	while( scanf("%d",&m) != EOF )
	{
		for( int i = 1; i <= 2*m; i ++ )
			map[i].clear();
		for( int i = 1; i <= m; i ++ )
		{
			scanf("%d",&temp);
			for( int j = 1; j <= temp; j ++ )
			{
				scanf("%d",&g);
				map[i].push_back(g+m);
			}
		}
		for( int i = 1; i <= m; i ++ )
		{
			scanf("%d",&g);
			map[g+m].push_back(i);
		}
		n = m*2;
		tarjan();
		for( int u = 1; u <= m; u ++ )
		{
			cnt.clear();
			for( int i = 0; i < map[u].size(); i ++ )
			{
				int v = map[u][i];
				if( p[u] == p[v] )
					cnt.push_back(v-m);
			}
			printf("%d",cnt.size());
			sort( cnt.begin(),cnt.end() );
			for( int j = 0; j < cnt.size(); j ++ )
				printf(" %d",cnt[j]);
			puts("");
		}
	}
    return 0;
}

#include<stdio.h>
#include<string.h>
#include<vector>
#include<queue>
using namespace std;
const int maxn = 5105;
const int inf = 1<<29;
int n;
int times,ans,top;
bool map[maxn][maxn],vis[maxn];
int low[maxn],dfn[maxn],stack[maxn],p[maxn];
void dfs(int u)
{
    dfn[u] = low[u] = ++times;                     //为节点u设定次序
    vis[u] = true;                             //判断是否在栈中
    stack[++top] = u;							  //将节点i压入栈中
    for ( int i = 1; i <= n; i ++ )						  //遍历每一条边
    {
        if( map[u][i] )
		{
			if (!dfn[i])					//如果节点i未被访问过
			{
				dfs(i);					 //继续向下查找
				low[u] = low[i] <= low[u]?low[i]:low[u];
			}
			else if ( vis[i] ) //如果节点i在栈中
				low[u] = dfn[i] <= low[u]?dfn[i]:low[u];
		}
    }
	int i;
    if ( dfn[u] == low[u] )     //如果节点i是强连通分量的根
    {
        ans++;                          //强连通分量数
        do
        {
            i = stack[top--];             //把栈里v上面的顶点弹栈
            p[i] = ans;
			vis[i] = false;
        }while ( i != u );
        
    }
}
void tarjan()
{
    int i;
    ans = times = top = 0;                //初始化
	memset( vis,0,sizeof(vis) );
    memset( dfn,0,sizeof(dfn) );
    for( i = 1; i <= n; i ++ )         //遍历每个节点
        if ( !dfn[i] )
            dfs(i);
}
int main()
{
    //freopen("data.txt","r",stdin);
	int m,temp,g;
	while( scanf("%d",&m) != EOF )
	{
		memset(map,0,sizeof(map));
		for( int i = 1; i <= m; i ++ )
		{
			scanf("%d",&temp);
			for( int j = 1; j <= temp; j ++ )
			{
				scanf("%d",&g);
				map[i][m+g] = true;
			}
		}
		for( int i = 1; i <= m; i ++ )
		{
			scanf("%d",&g);
			map[m+g][i] = true;
		}
		n = m*2;
		tarjan();
		vector<int>ans;
		for( int i = 1; i <= m; i ++ )
		{
			ans.clear();
			for( int j = m+1; j <= n; j ++ )
				if( map[i][j] && p[i] == p[j] )
					ans.push_back(j-m);
			printf("%d",ans.size());
			for( int j = 0; j < ans.size(); j ++ )
				printf(" %d",ans[j]);
			puts("");
		}
	}
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值