POJ2762 Going from u to v or from v to u?

题目大意 :

        给出一个有向图,问对于每一个顶点对(u,v),是否存在一条从u->v或一条从v->u的路。

解题思路 :

       网上思路是缩点+拓扑排序  。


非常抱歉,二分图最大匹配这个想法是错的,看下面一组测试数据就知道了。正解就是缩点+拓扑排序或找最长链。

1
5 8
5 3
2 1
5 1
1 3
4 3
3 2
3 1
3 5

AC代码在最后,中间是错误的想法和代码。

/****************************************************************************************************************************/

我的想法是求二分图的最大匹配。

这题可以看成求一个有向图中最小路径覆盖。

需要注意的是,这题输出Yes情况是:最大匹配>=n-1,而不是n。因为,n个点只需n-1边就可以连结起来。

建图方法:   每个点抽象成两个点,一个左,一个右。对于边,若u,v有边,则左边u连一条边到右边v的边。


上面的方法正确性是:只有一个连通图,存在多个时就不一定正确。而这题没说只有一个连通分支。感觉POJ这题数据有点水。如下这组上面就会输出Yes。

1
6 6
1 2
2 3
3 1
4 5
5 6
6 4

所以,在进行上面之前要判断无向图是否是一个连通的,不止一个连通分支的话一定输出No。

所以,加个对图连通分支判断即可。

错误代码(虽然可以AC):

#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
using namespace std;

#define clr(p,v) memset(p,v,sizeof(p))
const int maxn = 1010 ;
const int maxm = 6010 ;
const int inf = 1<<30 ;

int T,n,m,tot,tot2,cnt;
int linky[maxn],first[maxn],head[maxn];
int to[maxm],next[maxm];
int v[maxm<<1],next2[maxm<<1];
bool vis[maxn],fg;

void init()
{
    tot = tot2 = 0;
    clr(first,-1);
    clr(head,-1);
}

void add(int x,int y)
{
    to[tot]=y;next[tot]=first[x];first[x]=tot++;
}
/******************/
void add2(int x,int y)
{
    v[tot2]=y;next2[tot2]=head[x];head[x]=tot2++;
    v[tot2]=x;next2[tot2]=head[y];head[y]=tot2++;
}
/******************/
bool find(int cur)
{
    for(int i=first[cur];i!=-1;i=next[i])
    {
        int vv = to[i];
        if(vis[vv])
        {
            vis[vv] = false;
            if(linky[vv]==-1 || find(linky[vv]))
            {
                linky[vv] = cur;
                return true;
            }
        }
    }
    return false;
}

int hungary()
{
    int match = 0;
    clr(linky,-1);
    for(int i=1;i<=n;++i)
    {
        clr(vis,true);
        if(find(i))
            ++match;
    }
    return match;
}
/******************/
void dfs(int cur)
{
    vis[cur] = false;
    if(cnt == n)
    {
        fg = true;
        return;
    }
    //
    for(int i=head[cur];i!=-1;i=next2[i])
    {
        int vv = v[i];
        if(vis[vv])
        {
            ++cnt;
            dfs(vv);
        }
    }
}
/******************/
int main()
{
    //freopen("k","r",stdin);
    scanf("%d",&T);
    while(T--)
    {
        init();
        scanf("%d%d",&n,&m);
        while(m--)
        {
            int x,y;
            scanf("%d%d",&x,&y);
            add(x,y);
            add2(x,y);
        }
        /******************/
        clr(vis,true);
        fg = false;
        cnt = 1;
        dfs(1);
        /******************/
        if(fg && hungary() >= n-1) puts("Yes");
        else puts("No");
    }
    return 0;
}
/****************************************************************************************************************************/



AC代码:


#include<stdio.h>
#include<string.h>
#include<iostream>
#include<algorithm>
#include<map>
using namespace std;

#define clr(p,v) memset(p,v,sizeof(p))
const int maxn = 1010 ;
const int maxm = 10010 ;
const int inf  = 1<<30;

int n,m,tot,top,num;
int first[maxn],belg[maxn],dfn[maxn],low[maxn],in[maxn];
int to[maxm],next[maxm],stk[maxn*100];
int vis[maxn];
struct Edge
{
	int x,y;
}edge[maxm];

void init()
{
	tot = 0;
	clr(first,-1);
}

void add(int x,int y)
{
	to[tot]=y;next[tot]=first[x];first[x]=tot++;
}

void dfs(int cur,int &sig)
{
	dfn[cur] = low[cur] = ++sig;
	stk[++stk[0]] = cur;
	vis[cur] = 1;
	for(int i=first[cur];i!=-1;i=next[i])
	{
		int vv = to[i];
		if(vis[vv] == 0)
		{
			dfs(vv,sig);
			low[cur] = min(low[cur],low[vv]);
		}
		else if(vis[vv] == 1)
			low[cur] = min(low[cur],dfn[vv]);
	}
	if(dfn[cur] == low[cur])
	{
		num++;
		do
		{
			belg[stk[stk[0]]] = num;
			vis[stk[stk[0]]] = 2;
		}while(stk[stk[0]--]!=cur);
	}
}

void tarjan()
{
	int sig;
	clr(dfn,-1);
	clr(vis,0);
	num = sig = stk[0] = 0;
	for(int i=1;i<=n;++i) if(dfn[i] == -1)
		dfs(i,sig);
	//
	init();
	clr(in,0);
	for(int i=0;i<m;++i)
	{
		int x = belg[edge[i].x];
		int y = belg[edge[i].y];
		if(x != y)
		{
			add(x,y);
			++in[y];
		}
	}
}

bool solve()
{
	for(int uu=0;uu<num;++uu)
	{
		top = 0;
		for(int i=1;i<=num;++i)
		{
			if(in[i] == 0)
				stk[top++] = i;
		}
		if(top > 1) return false;
		for(int i=first[stk[0]];i!=-1;i=next[i])
		{
			int vv = to[i];
			--in[vv];
		}
		--in[stk[0]];
	}
	return true;
}

int main()
{
	//freopen("k","r",stdin);
	//freopen("out10.txt","w",stdout);
	int T;
	scanf("%d",&T);
	while(T--)
	//while(~scanf("%d%d",&n,&m))
	{
		init();
		scanf("%d%d",&n,&m);
		for(int i=0;i<m;++i)
		{
			int x,y;
			scanf("%d%d",&x,&y);
			add(x,y);
			edge[i].x = x;
			edge[i].y = y;
		}
		tarjan();
		if(solve()) puts("Yes");
		else puts("No");
	}
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值