题意:先给出离出火地点最近的路口,然后给出一些之间通畅的路口。要求给出所有从1号路口到火灾事故点的不含回路的简单路径。
思路:方法很容易想到,dfs即可。但是简单的dfs会超时,3s+. 之后看别人用了并查集,去了解了下并查集的思想。这里可以通过并查集提前判断一个路口是否和火灾路口想通,如果不通,则直接剪枝了。别看仅这一个优化,最后AC时间0.022,性能提升还是很多的。因为你早早地剪去一个结点,由它延伸下去的很多种情况都一并剪枝了的。也可以分析下直接dfs的复杂度,粗略地估算解答树结点的个数:1+20+19*19+19*18+...+19*1=3631个(最坏情况下,21个结点全连通)
并查集的思想见上篇博客讲解。这里写的并查集的代码也不是最优的union。
注意:PE错误提示了输出的时候是每个路径最后结点后面没有空格。
Code:
#include<stdio.h> #include<string.h> void dfs(int cur, int fr, int max); int find(int x); void unn(int x, int y); int p[25]; int g[25][25]; int vis[25];//不用0号结点,编号是从1到n int path[25]; int cnt; int num;//路径数 int main() { //freopen("208.in","r",stdin); //freopen("208.out","w",stdout); int fire; int a,b; int t=0; while(scanf("%d",&fire)==1) { //初始化 memset(g,0,sizeof(g)); memset(vis,0,sizeof(vis)); memset(path,0,sizeof(path)); for(int i=1;i<25;++i) p[i]=i; cnt=1; num=0; printf("CASE %d:\n",++t); int max=0; path[0]=1; vis[1]=1;//vis[1]不是vis[0] while(scanf("%d%d",&a,&b)==2 && a &&b) { max=a>max?a:max; max=b>max?b:max; g[a][b]=1; g[b][a]=1; unn(a,b); } dfs(1,fire,max); printf("There are %d routes from the firestation to streetcorner %d.\n",num,fire); } return 0; } void unn(int x, int y) { int xrt=find(x); int yrt=find(y); if(xrt!=yrt) { p[xrt]=yrt; } } int find(int x) { return x==p[x]?x:p[x]=find(p[x]); } void dfs(int cur, int fr, int max) { if(cur==fr) { num++; for(int i=0;i<cnt-1;++i) printf("%d ",path[i]); printf("%d\n",path[cnt-1]); return ; } for(int i=2;i<=max;++i) { int ffr=find(fr); if(g[cur][i] && !vis[i] && find(i)==ffr) { vis[i]=1; path[cnt++]=i; dfs(i,fr,max); //恢复 cnt--; vis[i]=0; } } }
原文地址:http://blog.csdn.net/buxizhizhou530/article/details/44227233