已知一个无向图,有村庄节点a个,有城堡节点b个,1到a表示村庄,a+1到a+b表示城堡,求第一个村庄节点到最后一个城堡节点的最短距离。如果某一段路径长度小于等于L且中间经过的节点(不包括两端)均是村庄类型,则可以穿一种神奇的靴子瞬间移动到那里,靴子只能在节点上使用或停止,靴子只能用K次
设共n个节点,对于如何使用这k次机会是一个动态规划问题,dp[ i ][ j ]表示从村庄1经过标号小于i的路径并使用j次穿靴子的机会到达点i的最短距离,则dp[ n ][ k ]为该问题最终结果,则状态转移方程为dp[ i ][ j ] = min( min ( dp[ k ][ j-1 ],dp[ k ][ j ] + dist[ k ][ i ] ) ),其中1<=k<i,dist数组表示最短路径。
使用该方程还需知道哪些节点之间可以使用靴子直接转移,在floyd计算过程中判断即可。
#include <stdio.h> #include <string.h> #define INF 10000 int dist[110][110],mark[110][110],dp[110][20]; /*mark[i][j]为1表示从i到j可以使用靴子,为0则不可以,dist为最短路径数组*/ int n,a,b,m,l,k; int min(int a,int b) { if(a>b) return b; else return a; } void floyd() { int i,j,q; for(q=1;q<=n;q++) { for(i=1;i<=n;i++) { for(j=1;j<=n;j++) { if(q==i||q==j) continue; if(dist[i][j]>dist[i][q]+dist[q][j]) dist[i][j]=dist[i][q]+dist[q][j]; if(dist[i][j]<=l&&q<=a) /*以节点q为中间节点,前a个是村庄,所以只要小于l即可,q大于a后,添 加的中间节点是城堡,所以不可以直接到达*/ mark[i][j]=1; } } } } void dynamic() { int i,j,q,min1=INF; for(i=1;i<=n;i++) dp[i][0]=dist[1][i]; for(i=0;i<=k;i++) dp[1][i]=0; for(i=2;i<=n;i++) { for(j=1;j<=k;j++) { min1=INF; for(q=1;q<i;q++) { if(mark[q][i]==1) min1=min(min1,dp[q][j-1]); min1=min(min1,dp[q][j]+dist[q][i]); } dp[i][j]=min1; } } } int main() { int t,i,j,q; int x,y,w; scanf("%d",&t); for(i=0;i<t;i++) { memset(mark,0,sizeof(mark)); scanf("%d%d%d%d%d",&a,&b,&m,&l,&k); n=a+b; for(j=1;j<=n;j++) for(q=1;q<=n;q++) dist[j][q]=INF; for(j=0;j<m;j++) { scanf("%d%d%d",&x,&y,&w); dist[x][y]=dist[y][x]=w; if(w<=l) mark[x][y]=mark[y][x]=1; } floyd(); dynamic(); printf("%d\n",dp[n][k]); } return 0; }