题目大意 :
给出一个有向图,问对于每一个顶点对(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;
}