题意:有m个城市,p条道路将他们连接起来,每经过一条道路需要缴纳车票,车票上印有马的匹数,因此通过一条道路等于道路距离/马的匹数,一共有n张票。问从a点到b点最少需要多少时间
假如不要车票的话,这道题无疑是最短路的算法。但是每条路均可以使用车票,假如将所有可行的方法保存下来,用贪心的思想将方法中经过的道路从长到短排序,票最多的匹配最长的道路,试试就逝世,再往下想会发现这程序写起来其实挺复杂冗长的。
状态压缩dp,一般是将状态转化为集合,利用集合来dp,在数据量较小的情况下,往往使用二进制来代替状态会很方便。
那么就可以将没车票当成数字1,有车票当成数字0(这样表示在本题中更方便),然后遍历所有车票状态(车站有30个,这样无法完全遍历,超时),dp【s】【v】,s表示车票状态,v表示现在在哪里,数组表示走到点v并且拥有s的车票的最短用时。起初将所有点设为inf(最大),然后遍历所有点,假如某个点的dp<inf,则以该点为中心更新其旁边的所有点
其实这道题挺裸的,会一些dp的我估计打打就能出来了
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int INF=1<<30;
const int maxn=1<<10; // city 太多,用ticket 可以
int ticket[10],dis[40][40];
double dp[maxn][31];
int main( ){
int n,m,p,a,b;
while(cin>>n>>m>>p>>a>>b){
if(n==0&&m==0&&p==0&&a==0&&b==0) break;
memset(dis,0,sizeof(dis));
memset(ticket,0,sizeof(ticket));
for(int i=0;i<n;i++) scanf("%d",&ticket[i]);
for(int i=1;i<=p;i++){
int fro,to,val;
scanf("%d %d %d",&fro,&to,&val);
dis[fro][to]=val;
dis[to][fro]=val;
}
for(int i=0;i<(1<<n);i++){
fill(dp[i]+1,dp[i]+m+1,INF);
}
dp[0][a]=0;
double res=INF;
for(int s=0;s<(1<<n);s++){ // 1: 无票,0:有票
for(int i=1;i<=m;i++){
for(int j=1;j<=m;j++){
if(dis[i][j]>0){
for(int k=0;k<n;k++){
if(!( s>>k & 1)) dp[s|(1<<k)][j]=min(dp[s|(1<<k)][j],dp[s][i]+(double)dis[i][j]/ticket[k]);
}
}
}
}
}
for(int i=1;i<(1<<n);i++){
res=min(res,dp[i][b]);
}
if(res!=INF) printf("%.3lf\n",res); //double 是 lf
else printf("Impossible\n");
}
return 0;
}