题意:有很多路,问能否每条路只走一遍、恰好回到起点?
思路:无向图的欧拉回路的应用。但是是神坑的一道题~
注意:考虑下面的数据
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错误)
反正就是感觉数据太坑了~~
首先,提交的是版本一,在判断连通性时,我没有用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 就可以了,没试过。。