多段图问题
【问题描述】如下图所示,多段图G=⟨V,E⟩是一个带权有向无环图(wDAG),其中顶点集可划分为k段,多段图问题即求s到t的最短路径。
现要求使用动态规划求解该问题。设cost(i,j)是结点j∈Vi到汇点t的最短路径的长度,则有 cost(i,j)=min{cost(i+1,p)+c(j,p)} (p为下一组中的点)
cost(k,t)=0
请使用上述动态规划思想,利用「备忘录方法」或者「迭代递推」实现对多段图问题进行编程求解。
输入格式:
第1行输入多段图顶点数m和边数n,以一个空格隔开。依次输入n条边的信息,每行输入一边条的顶点编号及权重,以一个空格隔开。
输出格式:
第1行输出s到t的一条最短路径(路径结点以一个空格隔开),第2行输出此最短路径的长度。
输入样例:
10 19
0 1 2
0 2 4
0 3 3
1 4 7
1 5 4
2 4 3
2 5 2
2 6 4
3 4 6
3 5 2
3 6 5
4 7 3
4 8 4
5 7 6
5 8 3
6 7 3
6 8 3
7 9 3
8 9 4
输出样例:
0 3 5 8 9
12
代码长度限制
16 KB
时间限制
400 ms
内存限制
64 MB
C语言实现:
#include<stdio.h>
#include<string.h>
int main(){
int n,m,i,j,k,h,sign,group_num,begin,end,min_dist,sec;
int A[100][100]; //边
int cost[100][100]; //迭代数组
int d[100][100]; //记录最优解
int v[100][100]; //点分组:v[i][]=5表示第i组中有点5
memset(A,-1,sizeof(A));
memset(cost,-1,sizeof(cost));
memset(d,-1,sizeof(d));
memset(v,-1,sizeof(v));
scanf("%d %d",&n,&m); //点数和边数
v[1][1]=0;
group_num=1; //目前begin属于第一组点
k=1;
for(i=0;i<m;i++){ //输入边的信息并对点进行分组
scanf("%d %d",&begin,&end);
scanf("%d",&A[begin][end]);
for(j=1;;j++){
if(v[group_num][j]==begin){ //begin为该组的点
sign=1;
for(h=1;;h++){
if(v[group_num+1][h]==end){
sign=0;
break;
}
if(v[group_num+1][h]==-1)
break;
}
if(sign==1)
v[group_num+1][k++]=end;
break;
}
if(v[group_num][j]==-1){ //begin为下一组中的点
group_num++;
k=1;
v[group_num+1][k++]=end;
break;
}
}
}
group_num++;
//检验分组
// printf("%d",group_num);
// for(i=1;i<=5;i++){
// for(j=1;j<=5;j++){
// printf("%d ",v[i][j]);
// }
// printf("\n");
// }
cost[group_num][v[group_num][1]]=0;
for(i=group_num-1;i>=1;i--){ //循环组
for(j=1;;j++){ //循环该组中的点
if(v[i][j]==-1) //该组的点处理完毕
break;
min_dist=1e6;
sec=-1;
for(k=1;;k++){ //循环下一组中的点
if(v[i+1][k]==-1)
break;
if(A[v[i][j]][v[i+1][k]]==-1)
continue;
if(min_dist>(cost[i+1][v[i+1][k]]+A[v[i][j]][v[i+1][k]])){
min_dist=cost[i+1][v[i+1][k]]+A[v[i][j]][v[i+1][k]];
sec=v[i+1][k];
}
}
cost[i][v[i][j]]=min_dist;
d[i][v[i][j]]=sec;
}
}
//检验cost数组
// for(i=1;i<=5;i++){
// for(j=0;j<=9;j++){
// printf("%d ",cost[i][j]);
// }
// printf("\n");
// }
sec=0;
printf("%d ",sec);
for(i=1;i<group_num;i++){
sec=d[i][sec];
printf("%d ",sec);
}
printf("\n%d",cost[1][0]);
return 0;
}
该代码对于点分组的处理过于繁琐,可以通过构建邻接表进行优化,后面有时间会进行优化。