UVA_208

这个题目思路很简单,但关键在于对深搜的优化(也即剪枝)。同时,这个题目好像没说最后要按字典序输出方案,但确实只有按字典序输出方案才能AC。(如果你看到了题目中有这点要求,千万别用鸡蛋砸我,我英文不好……)

一开始我用邻接矩阵写,超时了,后来改成了邻接表还是超时。看了别人的报告后,说是一开始先把到达起火点的所有点找出来,然后再从这些点中挑DFS时用到的点。

这样写完之后果然AC了,而且时间还很快,反思了一下,可能是测试数据中存在起点与终点不连通的数据,而与起点相连的又恰好是一个稠密图,反向搜索可达点、正项搜索路径恰好解决了这个问题。

其实在找能到达起火点的点时,也可以用并查集,个人感觉并查集实现起来要比DFS容易些。

但是,我们如果再想多一点,假如起火点是2,然后道路中有1条是1-2,其余便再也没有路和2相连,但1与一个十分稠密、顶点又多的图相连,那么即使我们做了上述的优化,估计也会超时的。因为我们做预处理时用的深搜或者并查集只能判断是否可能到达起火点,但不能断定在走不重复的点的情况一定可以到达起火点,这时在用DFS找起点与终点的道路的过程中,一旦DFS进入了与1相连的稠密图,那么就相当于进入了无底洞似的。

针对上面说的这类特殊情况,我想的一个办法就是先构造一条1与着火点之间的虚拟的通路,即1-22-着火点(不能直接构造1-着火点,因为有可能本来就存在这样一条路),之后便应用求边双连通的分量的tarjan算法求出包含1与着火点的边双连通分量,把这里面的所有点作为后续DFS选择的点。

在应用tarjan算法时,与我们常规求边双连通分量的方式有所不同,在main中,我们只序把1压栈,不再使用循环将!dfn[i]的点压栈,因为如果一个子图不和1以及着火点这个双连通分量相连,我们就没有必要去考虑这个子图了,这一点就相当于做了之前说的那一个优化。由于在应用tarjan算法求边双连通分量时有一个特性——最后栈里面会保留一个边双连通分量,而又因为我们一开始是先把1压栈的,所以最后保留的这个边双连通分量就是我们要求的边双连通分量,只要把这些栈中的点提取出来,进行排序,然后去掉22这个点就可以了。剩下的思路便和之前的一模一样了。

但如果你按我上面的思路这么做了,也是会超时的,因为我们是首先把1压栈的,如果存在1和着火点不连通的情况,那么当求到的边双连通分量中的顶点很多时,同样在DFS时会超时。所以,我们在最后出栈操作的时候,顺便寻找一下是否存在着火点,如果不存在,直接输出有0条路径就可以了。或者,我们也可以在第一次压栈的时候选择把着火点压栈。

前面讨论的两种思路的程序分别对应着下面的Program 1Program 2

// Program 1
#include<stdio.h>
#include
<string.h>
#include
<stdlib.h>
int G[25][25],vis[25],path[25],pn,p[25];
int A[25],n,fire;
int cmp(const void *_p,const void *_q)
{
int *p=(int *)_p;
int *q=(int *)_q;
return *p-*q;
}
int find(int x)
{
return p[x]==x?x:(p[x]=find(p[x]));
}
void dfs(int cur,int now)
{
int i;
if(now==fire)
{
n
++;
for(i=0;i<cur;i++)
{
if(i)
printf(
" ");
printf(
"%d",A[i]);
}
printf(
"\n");
return;
}
for(i=0;i<pn;i++)
if(G[now][path[i]]&&!vis[path[i]])
{
vis[path[i]]
=1;
A[cur]
=path[i];
dfs(cur
+1,path[i]);
vis[path[i]]
=0;
}
}
int main()
{
int i,j,k,u,v,t,x,y;
t
=0;
while(scanf("%d",&fire)==1)
{
printf(
"CASE %d:\n",++t);
memset(G,
0,sizeof(G));
while(1)
{
scanf(
"%d%d",&u,&v);
if(u==0)
break;
G[u][v]
=G[v][u]=1;
}
for(i=0;i<25;i++)
p[i]
=i;
for(i=0;i<25;i++)
for(j=i+1;j<25;j++)
if(G[i][j]&&find(i)!=find(j))
p[find(i)]
=find(j);
pn
=0;
for(i=0;i<25;i++)
if(find(i)==find(fire))
path[pn
++]=i;
qsort(path,pn,
sizeof(path[0]),cmp);
memset(vis,
0,sizeof(vis));
vis[
1]=1;
A[
0]=1;
n
=0;
dfs(
1,1);
printf(
"There are %d routes from the firestation to streetcorner %d.\n",n,fire);
}
return 0;
}

  

// Program 2
#include<stdio.h>
#include
<string.h>
#include
<stdlib.h>
int G[25][25],vis[25],path[25],p;
int A[25],n,fire,conect;
int dfn[25],low[25],time,s[25],top;
int cmp(const void *_p,const void *_q)
{
int *p=(int *)_p;
int *q=(int *)_q;
return *p-*q;
}
void tarjan(int fa,int u)
{
int v;
dfn[u]
=++time;
low[u]
=dfn[u];
for(v=0;v<25;v++)
if(G[u][v]&&v!=fa)
{
if(!dfn[v])
{
s[top
++]=v;
tarjan(u,v);
if(low[v]<low[u])
low[u]
=low[v];
else if(low[v]>dfn[u])
for(s[top]=-1;s[top]!=v;)
top
--;
}
else if(dfn[v]<low[u])
low[u]
=dfn[v];
}
}
void dfs(int cur,int now)
{
int i;
if(now==fire)
{
n
++;
for(i=0;i<cur;i++)
{
if(i)
printf(
" ");
printf(
"%d",A[i]);
}
printf(
"\n");
return;
}
for(i=0;i<p;i++)
if(G[now][path[i]]&&!vis[path[i]])
{
vis[path[i]]
=1;
A[cur]
=path[i];
dfs(cur
+1,path[i]);
vis[path[i]]
=0;
}

}
int main()
{
int i,j,k,u,v,t;
t
=0;
while(scanf("%d",&fire)==1)
{
printf(
"CASE %d:\n",++t);
memset(G,
0,sizeof(G));
while(1)
{
scanf(
"%d%d",&u,&v);
if(u==0)
break;
G[u][v]
=G[v][u]=1;
}
top
=time=0;
memset(dfn,
0,sizeof(dfn));
G[
1][22]=G[22][1]=G[fire][22]=G[22][fire]=1;
s[top
++]=1;
tarjan(
-1,1);
G[
1][22]=G[22][1]=G[fire][22]=G[22][fire]=0;
conect
=p=0;
while(top>0)
{
top
--;
if(s[top]==fire)
conect
=1;
path[p
++]=s[top];
}
if(!conect)
{
printf(
"There are 0 routes from the firestation to streetcorner %d.\n",fire);
continue;
}
qsort(path,p,
sizeof(path[0]),cmp);
p
--;
memset(vis,
0,sizeof(vis));
vis[
1]=1;
A[
0]=1;
n
=0;
dfs(
1,1);
printf(
"There are %d routes from the firestation to streetcorner %d.\n",n,fire);
}
return 0;
}

  

posted on 2011-09-07 22:34  Staginner  阅读(1034)  评论(0编辑  收藏  举报