ZOJ 1232

题目意思很简单:
有A个村子和B个城堡,村子标号是1~A,城堡标号是A+1~B。马里奥现在位于城堡B,他要带公主回到村子1,他有一双靴子,穿上之后可以不用时间就能从一个地方飞到另外一个地方,但是穿着靴子不能穿过城堡,穿靴子的次数也不能超过 K 次,一次不能超过 L km。求从 B 到 1 所用的最短时间。
分析:
最开始是在一本书上看到这道题目,说是Dijstra,于是打算来写写看。结果悲剧了。题目的关键点其实是在哪里使用鞋子才能使得全局的时间最少,可以用 DP 来解决,用一种逆向思维,先来写动态转移方程,用 i 来表示目前到了 i 点,用 j 来表示到 i 点时穿了几次靴子,dp[i][j] = min{dp[k][j-1],dp[k][j]+map[k][i]} 从方程中可以看出,需要知道任意两个点之间的距离,也需要知道任意两个点之间是否能够穿靴子通过(比如考虑到 K,L),此时就需要用到求最短路径了,由于此时用到的是任意两个点之间的最短路径,所以最好用 Floyd ,用 Dijstra 的话在求任意两个点之间是否能够穿靴子通过时就会出现点小麻烦。
好吧,其实我两种都写了。

贴下Dijstra的代码

#include<stdio.h>
const int maxn = 150;
const int INF = 1000000000;
int mat[maxn][maxn],tmp[maxn][maxn],Dist[maxn];
int DP[maxn][20],newmat[maxn][maxn];
bool visited[maxn];
int n,A,B,L;
void Dijstra(int s)
{
	int i,j,k,min;
	for(i=1;i<=n;i++)
	{
		Dist[i] = mat[s][i];
		visited[i] = false;
	}
	Dist[s] = 0;
	visited[s] = true;
	for(i=1;i<=n;i++)
	{
		k = -1;
		min = INF;
		for(j=1;j<=n;j++)
			if(visited[j]==false&&min>Dist[j])
			{
				k = j;
				min = Dist[j];
			}
			if(k!=-1)
			{
				visited[k] = true;
				for(j=1;j<=n;j++)
				{
					if(Dist[k]+mat[k][j]<Dist[j])
					{
						Dist[j] = Dist[k] + mat[k][j];
					}
				}
			}
	}
}

void Dijstra1(int s)//为了求tmp[][],tmp[][]保存的是任意两点是否能穿鞋子通过。
{
	int i,j,k,min;
	for(i=1;i<=n;i++)
	{
		Dist[i] = mat[s][i];
		visited[i] = false;
	}
	Dist[s] = 0;
	visited[s] = true;
	for(i=1;i<=n;i++)
	{
		k = -1;
		min = INF;
		for(j=1;j<=n;j++)
			if(visited[j]==false&&min>Dist[j])
			{
				k = j;
				min = Dist[j];
			}
			if(k!=-1)
			{
				visited[k] = true;
				for(j=1;j<=n;j++)
				{
					if(Dist[k]+mat[k][j]<Dist[j]&&k<=A)
					{	
						Dist[j] = Dist[k]+mat[k][j];
						if(Dist[j]<=L)
							tmp[s][j] = 0;
					}
				}
			}
	}
}
int main()
{
	int i,j,t,m,K;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d%d%d%d",&A,&B,&m,&L,&K);
		n = A+B;
		for(i=1;i<=n;i++)
			for(j=1;j<=n;j++)
				mat[i][j] = newmat[i][j] = tmp[j][i] = INF;//初始化
			int a,b,c;
			for(i=0;i<m;i++)
			{
				scanf("%d%d%d",&a,&b,&c);
				mat[a][b] = mat[b][a] = c;
				if(c<=L)
					tmp[a][b] = tmp[b][a] = 0;
				newmat[a][b] = newmat[b][a] = c;
			}//构图
			for(i=1;i<=n;i++)
			{
				Dijstra1(i);
				Dijstra(i);
				for(j=1;j<=n;j++)
					newmat[i][j] = Dist[j];
			}
			for(i=1;i<=n;i++)
				DP[i][0] = newmat[1][i];//未使用加速靴时的时间
			for(j=1;j<=K;j++)
				DP[1][j] = 0;
		    for(i=2;i<=n;i++)	
			{
				for(j=1;j<=K;j++)
				{
					int min = INF,tt;
					for(int l = 1;l<i;l++)
					{
						if(!tmp[l][i])
							tt = DP[l][j-1]>DP[l][j]+newmat[l][i]?DP[l][j]+newmat[l][i]:DP[l][j-1];
						else 
							tt = newmat[l][i]+DP[l][j];
						if(tt<min)
							min = tt;
					}
					DP[i][j] = min;
				}
			}
			printf("%d\n",DP[n][K]);
			
	}
	return 0;
}
再来比对一下Floyd的代码
#include<stdio.h>
const int maxn = 150;
const int INF = 1000000000;
int dp[maxn][20],mat[maxn][maxn],mind[maxn][maxn],tmp[maxn][maxn];
int n,A,B,L;
void Floyd()
{
	int i,j,k;
	for(i=1;i<=n;i++)
		for(j=1;j<=n;j++)
			mind[i][j] = mat[i][j];
	for(k=1;k<=n;k++)
	{
		for(i=1;i<=n;i++)
		{
			for(j=1;j<=n;j++)
			{
				if(mind[i][k]+mind[k][j]<mind[i][j])
				{
					mind[i][j] = mind[i][k] + mind[k][j];
					if(k<=A&&mind[i][j]<=L)
						tmp[i][j] = 1;
				}
			}
		}
	}

}
int main()
{
	int t,K,m,i,j,a,b,c;
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d%d%d%d",&A,&B,&m,&L,&K);
		n = A+B;
		for(i=1;i<=n;i++)
		{
			for(j=1;j<=n;j++)
			{
				if(i==j)
					mat[i][j] = 0;
				else
					mat[i][j] = INF;
				tmp[i][j] = 0;
			}
		}//初始化
		for(i=1;i<=m;i++)
		{
			scanf("%d%d%d",&a,&b,&c);
			mat[a][b] = mat[b][a] = c;
			if(c<=L)
				tmp[a][b] = tmp[b][a] = 1;
		}//初步构图
		Floyd();//mind[][]保存计算之后的最短路径
		for(i=1;i<=n;i++)
			dp[i][0] = mind[1][i];
		for(i=1;i<=K;i++)
			dp[1][i] = 0;
		for(i=2;i<=n;i++)
		{
			for(j=1;j<=K;j++)
			{
				int min = INF,tt;
				for(int l=1;l<i;l++)
				{
					if(tmp[l][i])
						tt = dp[l][j-1]<dp[l][j]+mind[l][i]?dp[l][j-1]:dp[l][j]+mind[l][i];
					else
						tt = dp[l][j] + mind[l][i];	
					if(tt<min)
						min = tt;
				}
				dp[i][j] = min;
			}
		}
		printf("%d\n",dp[n][K]);
	}
	return 0;
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值