Description
In the town there are N crossing points numbered from 1 to N and M two-way roads numbered from 1 to M. Two crossing points can be connected by multiple roads, but no road connects a crossing point with itself. Each sightseeing route is a sequence of road numbers y_1, ..., y_k, k>2. The road y_i (1<=i<=k-1) connects crossing points x_i and x_{i+1}, the road y_k connects crossing points x_k and x_1. All the numbers x_1,...,x_k should be different.The length of the sightseeing route is the sum of the lengths of all roads on the sightseeing route, i.e. L(y_1)+L(y_2)+...+L(y_k) where L(y_i) is the length of the road y_i (1<=i<=k). Your program has to find such a sightseeing route, the length of which is minimal, or to specify that it is not possible,because there is no sightseeing route in the town.
Input
Output
Sample Input
5 7
1 4 1
1 3 300
3 1 10
1 2 16
2 3 100
2 5 15
5 3 20
Sample Output
1 3 5 2
题意:
有n个城市,有m条路,每条路连接两个城市而且有一定的花费,你的任务是找出一条由起点开始又回到起点的路径,使得路径上所有的花费最小,并且这条路径至少有3个以上不同城市,假如从v1开始,那么v1->v2->....->vk,vk再回到v1,k要大于2,输出格式为v1 v2 ....vk
其中n<=100,m<=10000,每条路径花费不大于500;
分析:如果不输出路径,那么此题就是一个floyd求最小环的问题,但是此题需要输出的是最小环经过的点,所以我们就要在原来求最小环的基础上加一个数组记录路径,我们另外开一个数组pa[i][j]=x表示的是i->......->x->j,意思就是pa[i][j]存的是i->j这条路径上j的前面一个点是哪个。
很显然,初始化时pa[i][j]=i;
那么如何用dp转移呢,回顾一下求floyd求最短路时转移是这样的:如果dis[i][j]>dis[i][k]+dis[k][j],那么dis[i][j]=dis[i][k]+dis[k][j],我们可以在这个时候同时转移pa,使pa[i][j]=pa[k][j],仔细想一下是不是这样,i->j这条路径上倒数第二个经过的点就是k->j上倒数第二个经过的点,因为pa[i][k]和pa[k][j]已经求出来了的,所以直接转移。
转移完成了,接下来的工作就是该怎么记录答案并且输出答案。我们只需要在floyd求最小环记录答案的时候把路径记录下来。
具体操作代码实现:
#include<Stdio.h>
#include<string.h>
#include<algorithm>
#define inf 1e8
using namespace std;
int dis[110][110];///记录最短路
int pa[110][110];///记录i>j这条路径上倒数第二个经过的点
int ma[110][110];///记录原图
int ans[110];///记录路径(倒着的)
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)==2)
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
dis[i][j]=ma[i][j]=inf;
pa[i][j]=i;
}
}
for(int i=1;i<=m;i++)
{
int x,y,c;
scanf("%d%d%d",&x,&y,&c);
if(ma[x][y]>c)///防止重边
{
ma[x][y]=dis[x][y]=c;
ma[y][x]=dis[y][x]=c;
}
}
int minn=inf,num;///num记录的是路径上点的个数
for(int k=1;k<=n;k++)
{
///下面这两个循环意思是以k为起点,看看有没有回到k点的路径,i和j表示的是和k点直接相连的不同的两个点,这样保证了这个换有三个点
for(int i=1;i<k;i++)
{
for(int j=i+1;j<k;j++)
{
if(minn>dis[i][j]+ma[k][i]+ma[k][j])
{
num=0;///每次找到更好的答案时便要重新记录路径
minn=dis[i][j]+ma[k][i]+ma[k][j];
int posi=i,posj=j;
ans[++num]=j;
while(1)///倒着记录路径,画个图自然明白
{
posj=pa[posi][posj];
num++;
ans[num]=posj;
if(posi==posj) break;
}
ans[++num]=k;
}
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
if(dis[i][j]>dis[i][k]+dis[k][j])
{
dis[i][j]=dis[i][k]+dis[k][j];
pa[i][j]=pa[k][j];
}
}
}
}
if(minn==inf)
{
puts("No solution.");
continue;
}
for(int i=num;i>=2;i--) printf("%d ",ans[i]);
printf("%d\n",ans[1]);
}
return 0;
}