题意:先给出离出火地点最近的路口,然后给出一些之间通畅的路口。要求给出所有从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;
}
}
}