zoj 1232 Adventure of Super Mario

已知一个无向图,有村庄节点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;
}


 

你可能感兴趣的:(zoj 1232 Adventure of Super Mario)