动态规划--多段图问题

多段图问题
【问题描述】如下图所示,多段图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;
}

该代码对于点分组的处理过于繁琐,可以通过构建邻接表进行优化,后面有时间会进行优化。

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值