题意:有很多路,问能否每条路只走一遍、恰好回到起点?
思路:无向图的欧拉回路的应用。但是是神坑的一道题~
注意:考虑下面的数据
Input:
3 2
0 1
1 0
2 2
1 0
1 0
4 4
0 1
1 0
2 3
3 2
5 6
0 1
1 0
2 3
2 3
0 2
2 0
4 6
1 2
2 1
2 3
2 3
3 1
1 3
2 0
Output:
Possible
Possible
Not Possible
Possible
Possible
Not Possible
也就是说,第一,只要把所有的边访问就行了,可以存在一些没有访问的顶点。
第二,r==0时是Not Possible的(没有路径时就不能访问所有的边,因为没有边可访问;但是没有路径时也等于可以访问所有的边,因为没有边是没有访问的。。感觉和第一条的逻辑有些矛盾!!)
第三,输入数据可能存在自回路,也就是 1 1这样的边,它是考虑到有一条路是圆形的吗??(这个导致了版本一的RE错误)
总结:输入无向边,graph[u][v]++; graph[v][u]++; (不要用graph[v][u]=graph[u][v]; 因为对自回路错误。) 修改相应顶点的度数
删除无向边,graph[u][v]--; graph[v][u]--;
反正就是感觉数据太坑了~~
首先,提交的是版本一,在判断连通性时,我没有用vis数组来判断一个顶点是否访问过,而是用顶点的度数是否为0来判断,因为每次访问一条边后、删除这条边时会减少相应顶点的度数。但是在输入时我用的graph[u][v]++;graph[v][u]=graph[u][v]; 提交后RE错误,由于题目在终止输入时没有说清,改了几次输入如下代码所示,变成了WA。
Code:
//版本一,AC
#include<stdio.h>
#include<string.h>
void dfs(int u,int n);
bool solve(int n);
int du[205];
int graph[205][205];
int main()
{
int n,r;
//scanf("%d%d",&n,&r);
//while(scanf("%d%d",&n,&r)==2 && n && r)
while(scanf("%d%d",&n,&r)==2)
{
memset(du,0,sizeof(du));
memset(graph,0,sizeof(graph));
for(int i=0;i<r;++i)
{
int u,v;
scanf("%d%d",&u,&v);
graph[u][v]++;
//graph[v][u]=graph[u][v];
graph[v][u]++;
du[u]++;
du[v]++;
}
if(solve(n)) printf("Possible\n");
else printf("Not Possible\n");
}
return 0;
}
bool solve(int n)
{
for(int i=0;i<n;++i)
{
if(du[i]&1) return false;
}
int cnt=0;
for(int i=0;i<n;++i)
{
if(du[i])
{ cnt++; dfs(i,n);}
}//printf("cnt:%d\n",cnt);
if(cnt!=1) return false;
return true;
}
void dfs(int u,int n)
{
for(int v=0;v<n;++v)
{
if(graph[u][v])
{ //删除无向边
graph[u][v]--;
graph[v][u]--;
du[u]--;
du[v]--;
dfs(v,n);
}
}
}
版本二:版本二在判断连通性时,不是用dfs数总共的连通分量个数了,而是一遍dfs后看是否有结点未访问,WA了。(这里也是在访问边时修改顶点的度数,最后用顶点的度数判断顶点是否访问过。)不过这一版本的调试,发现了如果在输入时用graph[u][v]++; graph[v][u]=graph[u][v];程序会崩溃,原因是如果有顶点的自回路,则上述语句的真正意思错了,而是应该用graph[u][v]++; graph[v][u]++; (不过因为这两种方式都提交了,都得到了WA,所以没有想到版本一的RE正是这个原因!!!) 版本一的输入部分,由前者改为了后者,则AC了,如上述代码所示。 (写版本二的原因是,看了网上一个用这种方式判断连通性的,而且实在想不通版本一的错误在哪。 但是最后试了下,参考的那个代码竟然是WA的。。。可能该题后来re-judge了。。)
//WA,别再修改
#include<stdio.h>
#include<string.h>
void dfs(int u,int n);
bool solve(int n);
int du[205];
int graph[205][205];
int main()
{
int n,r;
//scanf("%d%d",&n,&r);
//while(scanf("%d%d",&n,&r)==2 && n && r)
while(scanf("%d%d",&n,&r)==2)
{
memset(du,0,sizeof(du));
memset(graph,0,sizeof(graph));
for(int i=0;i<r;++i)
{
int u,v;
scanf("%d%d",&u,&v);
graph[u][v]++;
//graph[v][u]=graph[u][v];
graph[v][u]++;//自回路
du[u]++;
du[v]++;
}
if(solve(n)) printf("Possible\n");
else printf("Not Possible\n");
}
return 0;
}
bool solve(int n)
{
for(int i=0;i<n;++i)
{
if(du[i]&1 || du[i]==0) return false;
}
dfs(0,n);
for(int i=0;i<n;++i)
{ if(du[i]) return false; }
return true;
}
void dfs(int u,int n)
{
for(int v=0;v<n;++v)
{
if(graph[u][v])
{ //删除无向边
graph[u][v]--;
graph[v][u]--;
du[u]--;
du[v]--;
dfs(v,n);
}
}
}
版本三:版本三是在版本二的基础上修改。版本二的错误一在于,dfs(0)的错误。因为0号顶点可能是没有边的。错误二在于,r==0时是Not Possible的。
<pre name="code" class="cpp">//AC
#include<stdio.h>
#include<string.h>
void dfs(int u,int n);
bool solve(int n);
int du[205];
int graph[205][205];
int vis[205];
int main()
{
int n,r;
//scanf("%d%d",&n,&r);
//while(scanf("%d%d",&n,&r)==2 && n && r)
while(scanf("%d%d",&n,&r)==2)
{
memset(du,0,sizeof(du));
memset(graph,0,sizeof(graph));
memset(vis,0,sizeof(vis));
for(int i=0;i<r;++i)
{
int u,v;
scanf("%d%d",&u,&v);
graph[u][v]++;
//graph[v][u]=graph[u][v];
graph[v][u]++;//自回路
du[u]++;
du[v]++;
}
if(r && solve(n)) printf("Possible\n");
else printf("Not Possible\n");
}
return 0;
}
bool solve(int n)
{
for(int i=0;i<n;++i)
{
if(du[i]&1) return false;
}
for(int i=0;i<n;++i)
{
if(du[i])
{
vis[i]=1;
dfs(i,n);
break;
}
}
for(int i=0;i<n;++i)
{ if(du[i] && !vis[i]) return false; }
return true;
}
void dfs(int u,int n)
{
for(int v=0;v<n;++v)
{
if(!vis[v] && graph[u][v])
{
vis[v]=1;
dfs(v,n);
}
}
}
另外,据说只要判断每个顶点的度数是否为偶数以及 r 是否等于 1 就可以了,没试过。。