// 挑战上的状压例题,感觉思路挺清晰,但是很难想...
// 不过学到了一个套路,那就是转换成DAG进行DP,很妙
// 车票状态为第一维度,顶点为第二维度。
// DP[S][u]表示在u点时,车票状态集合为S的最小花费。
// 则状态转移为在u点找一张车票i,找一个点v.
// DP[S \ i][v] = min(DP[S][u] + dist[u][v] / T[i]);
// 起始状态DP[所有车票][a] = 0;其他为无穷大。
// 结果为 min(dp[车票][b]); 因此集合应该从大到小枚举
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 10;
const int maxm = 40;
const int INF = 0x7fffffff;
int mp[maxm][maxm];
int n,m,a,b,p;
int T[maxn];
double dp[1 << maxn][maxm];
void DP(){
for (int i = 0;i < (1 << n);i ++)
fill(dp[i],dp[i] + m,INF);
dp[(1 << n) - 1][a - 1] = 0;
for (int S = (1 << n) - 1; S >= 0; S --){
for (int u = 0; u < m;u ++){
for (int i = 0;i < n;i ++){
if ((S >> i) & 1){
for (int v = 0; v < m; v++){
if (mp[v][u] > 0){
dp[S & ~(1 << i)][u] = min(dp[S & ~(1 << i)][u],
dp[S][v] + mp[v][u] *1.0 / T[i]);
}
}
}
}
}
}
double res = INF;
for (int i = 0;i < 1 << n;i ++)
res = min(res,dp[i][b - 1]);
if (res < INF){
printf("%.3lf\n",res);
}else {
puts("Impossible");
}
}
int main(){
//freopen("1.txt","r",stdin);
while(scanf("%d%d%d%d%d",&n,&m,&p,&a,&b)!=EOF){
if (n == 0 && m == 0)
break;
for (int i = 0;i < n;i ++)
scanf("%d",&T[i]);
memset(mp,0,sizeof(mp));
for (int i = 0;i < p;i ++){
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
mp[x - 1][y - 1] = mp[y - 1][x - 1] = z;
}
DP();
}
return 0;
}
poj 2686 状压DP
最新推荐文章于 2020-05-04 08:25:36 发布