题意:
有一个旅行家计划乘马车旅行。他所在的国家共有m个城市,在城市之间有p条道路相连接。从某个城市沿着某条道路到相邻的城市需要乘坐马车。而乘坐马车需要使用车票,每用一张车票只可以通过一条道路。每张车票上都记有马的匹数,从一个城市移动到另一个城市的所需时间等于城市之间道路的长度除以马的数量的结果。这位旅行家一共有n张车票,第i张车票上的马的匹数是ti.一张车票只能用一次,并且换乘所需时间可以忽略。求从城市a到城市b所需要的最短时间。如果无法到达城市b输出"Impossible"。
思路:
虽然可以把城市当作顶点,道路看成边建图,但是由于车票相关的限制,无法直接用算法求解。应为比较小,可以考虑一下状压,二进制位表示票取还是不取,用集合表示。将状态看成顶点,转移看成一条边,就可以用算法求解了,但是很浪费,计算的最短路可以简单地通过求解。
状态:表示到达城市,剩下的车票集合为的最小花费
code:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> #include<queue> #include <vector> #include<map> #include<set> #include<utility> using namespace std; typedef long long ll; const int maxn=10,maxm=35,inf=0x3f3f3f3f; inline ll read() { ll s = 0, w = 1; char ch = getchar(); while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); } while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar(); return s * w; } int n,m,a,b; int t[maxn]; int d[maxm][maxm]; double dp[1<<maxn][maxm]; void solve() { for(int i=0; i< 1<<n; ++i) fill(dp[i],dp[i]+m,inf); dp[(1<<n)-1][a-1]=0; double res=inf; for(int s=(1<<n)-1; s>=0; --s) { res=min(res,dp[s][b-1]); for(int v=0; v<m; ++v) { for(int i=0; i<n; ++i) { if(s>>i&1) { for(int u=0; u<m; ++u) { if(d[v][u]>=0) { //使用车票i,从v移动到u dp[s&~(1<<i)][u]=min(dp[s&~(1<<i)][u],dp[s][v]+(double)d[v][u]/t[i]); } } } } } } if(res==inf) puts("Impossible"); else printf("%.3f\n",res); } int main() { int p; while(1) { n=read(),m=read(),p=read(),a=read(),b=read(); if(n==0&&m==0) return 0; for(int i=0; i<n; ++i) t[i]=read(); memset(d,-1,sizeof d); for(int i=0,u,v; i<p; ++i) { u=read()-1,v=read()-1; d[u][v]=d[v][u]=read(); } solve(); } }