AOJ 2200 Mr. Rito Post Office——floyd+动态dp

这道题做了一天,踩了不少坑。。呜呜呜

题目大意

快递到了:你是某个岛国(ACM-ICPC Japan)上的一个苦逼程序员,你有一个当邮递员的好基友利腾桑遇到麻烦了:全岛有一些镇子通过水路和旱路相连,走水路必须要用船,在X处下船了船就停在X处。而且岛上只有一条船,下次想走水路还是得回到X处才行;两个镇子之间可能有两条以上的水路或旱路;邮递员必须按照清单上的镇子顺序送快递(镇子可能重复,并且对于重复的镇子不允许一次性处理,比如ABCB的话B一定要按顺序走两次才行)。

测试数据有多组:

N M

x1 y1 t1 sl1

x2 y2 t2 sl2

xM yM tM slM

R

z1 z2 … zR

N (2 ≤ N ≤ 200) 是镇子的数量,M (1 ≤ M ≤ 10000) 是旱路和水路合计的数量。从第2行到第M + 1行是路径的描述,路径连接xi yi两地,路径花费 ti (1 ≤ ti ≤ 1000)时间,sli 为L时表示是旱路,S时表示是水路。可能有两条及以上路径连接两个镇子,并且路径都是双向的。

M + 2行的R是利腾需要去的镇子的数量,M + 3是利腾需要去的镇子的编号。

初始状态利腾和船都在第一个镇子,且肯定有方法达到需要去的镇子。

测试数据为0 0的时候表示终止。

题目解析

  • 这道题难度不小,因为不再是单纯的套个floyd最短路径就完事了,而是需要神器动态dp的关键绝杀。
  • 起初的简单想法是:从A走到B,如果能走水路和旱路,那么取二者最小,如果不能的话,那就没得选了。但是,问题就难在,问题不能考虑眼前,不是贪心做法,而是全局考虑,因为你可能没路走,还得跑到其他路走,然后还得考虑水路、旱路,所以只能用dp了
  • dp状态方程,得体现出走到哪里了吧,因为还得考虑船的位置,所以dp方程还得考虑船的位置,所以,方程就出来了。dp【i】【j】表示到达目的地i而且船停在j地所走的路程。
  • 而在状态转移过程中,需要考虑两种情况,一种是走旱路,一种是兼顾水路。
    1. dp[i][j]=min(dp[i][j],dp[i-1][j]+l[def[i-1]][def[i]]);
      这个是只考虑旱路,从i-1到i走旱路,这里的i和i-1是def中的顺序对应的地点,而不是实际地点
    2. for(int k=1; k<=n; k++) {
      dp[i][k]=min(dp[i][k],dp[i-1][j]+l[def[i-1]][j]+s[j][k]+l[k][def[i]]);
      }
      这个是为下一个目的地做准备,也就是为下一次考虑上水路的情况。dp【i-1】【j】表示到了i-1,船在j,那就考虑把船开到k地,然后再走到i地。
    3. 记得还要初始化初始地方,就是dp【def【1】】【i】,在第一次情况下,就要把船停到所有地方的情况都考虑到。
      记得是def【1】,而非实际地点1,我把题目这个理解错,搞了很长时间都是wa,哎,心累!!
    4. 而且dp数组必须得是long long类型,不然几个0x3f3f3f3f相加,很容易int溢出。。。。

贴上代码把

#include<iostream>
#include<cstdio>
#include<string>
#include<limits.h>
#include<algorithm>
#include<cstring>
#include<math.h>
#include<set>
#include<vector>
#include<queue>
#include<set>
#include<stdlib.h>
#include<time.h>
using namespace std;
typedef long long ll;
#define inf	0x3f3f3f3f
typedef pair<int,int>	P;
int n,m;
int r;
int def[1001];
/*struct Road{
	int ldis;
	int sdis;
};
vector<Road> roads[300][300];
*/
int l[300][300];
int s[300][300];
ll dp[1010][300];
void init() {
	//memset(,inf,sizeof(road));
	memset(l,inf,sizeof(l));
	memset(s,inf,sizeof(s));
	memset(dp,inf,sizeof(dp));
	for(int i=1; i<=n; i++) {
		l[i][i]=0;
		s[i][i]=0;
	}
}

void floyd() {
	for(int k=1; k<=n; k++) {
		for(int i=1; i<=n; i++) {
			for(int j=1; j<=n; j++) {
				if(l[i][j]>l[i][k]+l[k][j]) {
					l[i][j]=l[i][k]+l[k][j];
				}
				if(s[i][j]>s[i][k]+s[k][j]) {
					s[i][j]=s[i][k]+s[k][j];
				}
			}
		}
	}
}

ll solve() {
	floyd();
	for(int i=1; i<=n; i++) {
		dp[1][i]=s[def[1]][i]+l[i][def[1]];
	}
	for(int i=2; i<=r; i++) {
		for(int j=1; j<=n; j++) {	//上次停船地方
			//分析旱路
			dp[i][j]=min(dp[i][j],dp[i-1][j]+l[def[i-1]][def[i]]);
			//将船停到新地方,为下一次做准备
			for(int k=1; k<=n; k++) {
				dp[i][k]=min(dp[i][k],dp[i-1][j]+l[def[i-1]][j]+s[j][k]+l[k][def[i]]);
			}
		}
	}
	
	return *min_element(dp[r]+1,dp[r]+1+n);
}

int main() {
	int x,y,t;
	char str[2];
	//cout<<(ll)inf<<endl;
	while(scanf("%d%d",&n,&m)!=EOF) {
		if(n==0&&m==0){
			break;
		}
		init();
		while(m--) {
			scanf("%d%d%d%s",&x,&y,&t,str);
			if(str[0]=='L') {
				l[x][y]=l[y][x]=t;
			} else if(str[0]=='S') {
				s[x][y]=s[y][x]=t;
			}
		}
		scanf("%d",&r);
		for(int i=1; i<=r; i++) {
			scanf("%d",&def[i]);
		}
		printf("%lld\n",solve());
	}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值