民大风景
总提交 : 76 测试通过 : 17
描述
民大新校区中有n个景点可去,比如小镇,南大门,映月潭,梦云湖等等。YKY近来闲的无事,便想游历一下校园。景点之间都有一条最短的直通的路,然而YKY只会去走其中一些,因为“妹子多”,YKY说“妹子之所在,便是我的去处”。由于太过DS,YKY懒得走太多路,又不愿去重复的地方,他只想绕尽量小的圈回到原地。比如他从v1点出发,最后由v1,v2,...vk,v1回到v1(k>=3)。现在YKY需要你帮他找一条这样的路线,并且路程最短。
输入
输出
对于每个测试实例,如果能找到这样一条路线的话,输出路程的最小值。如果找不到的话,输出"It's impossible.".
样例输入
3 3
1 2 1
2 3 1
1 3 1
3 3
1 2 1
1 2 3
2 3 1
样例输出
3
It's impossible.
这题的题意就是判断一个树中的最短环。开始没做出来是没有理解prim算法
prim算法就是三个for循环:
for(k=1;k<=n;k++)
for(i=1;i<=n;i++)
for(j=1;j<=i;j++)
第一个K表示i点到j点经过小于等于k点的点的最短路径,这样一步步循环到k=n时即为i到j点经过N个点的最短路径。
我以前只是硬记住了算法程序,没有弄清楚本质,这题做时就不知道该怎么办。
起初想的是求出dis[I][I]就行了,但可能出现回环的可能,即1—》2然后2—》1。也想到判断dis[i][k]!=dis[k][i],但显然是错的,碰巧一样了怎么办。于是思路陷入了死路。后来看了别人代码,发现是求出dis[i][j]后一个个寻找中间点K,如果K!=i&&k!=j,那么就不会出现回环了。值得注意的是求出小于等于K的dis[i][j]后不能继续判断经过小于K的点的最短环,因为dis[i][j]中可能已经经过了K点,所以要判断经过K+1的点的最短环。
也就是说:
求dis[i][j]经过K点的最短环,那么这时的dis[i][j]必须是中间点小于K的最短路径。所以可以在一个循环里面把求最短路和最短环写在一起。
AC代码如下:
#include<stdio.h>
#include<iostream>
using namespace std;
const int Max=110;
int Map[Max][Max],dis[Max][Max],ans,n;
int minn(int x,int y)
{
return x>y?y:x;
}
void Floyd()
{
int i,j,k;
for(i=1;i<=n;i++)
for(j=i+1;j<=n;j++)
if(i!=1&&j!=1)
ans=minn(ans,dis[i][j]+Map[i][1]+Map[j][1]);
for(k=1;k<=n;k++)
{
for(i=1;i<=n;i++)
{
for(j=1;j<i;j++)
{
if(dis[i][j]>dis[i][k]+dis[k][j])
dis[i][j]=dis[j][i]=dis[i][k]+dis[k][j];
// cout<<dis[j][i]<<" ";
if(i!=k+1&&j!=k+1&&k+1<=n)
ans=minn(ans,dis[i][j]+Map[i][k+1]+Map[j][k+1]);
// cout<<ans<<endl;
}
// cout<<endl;
}
}
}
int main()
{
int m,i,j,a,b,c;
while(~scanf("%d%d",&n,&m))
{
for(i=1;i<=n;i++)
for(j=1;j<=n;j++)
{
if(i==j)
{
Map[i][j]=dis[i][j]=0;
continue;
}
Map[i][j]=1000000;
dis[i][j]=1000000;
}
while(m--)
{
scanf("%d%d%d",&a,&b,&c);
if(c<Map[a][b])
{
Map[a][b]=Map[b][a]=c;
dis[a][b]=dis[b][a]=c;
}
}
if(n<=2)
{
printf("It's impossible.\n");
continue;
}
ans=1000000;
Floyd();
if(ans>=1000000)
printf("It's impossible.\n");
else
printf("%d\n",ans);
}
return 0;
}