◆POJ 2686◆
Traveling by Stagecoach
□题目□
题目太长了……不方便copy,戳下面看原题(Vjudge)
Vjudge-POJ 2686
□大致翻译□
一个旅行家在m个城市(城市的编号范围: 1~30)中旅行;有p条路,给出每条路的起点和终点以及长度;他有n张票,每张票上有一个数值,通过一条路需要一张票,且通过这条路的花费为 “路的长度/票的数值”。求旅行家从城市A到城市B的最小花费。
□解析□
这道题比较像旅行家问题,同样是从城市A到城市B((∩_∩)),但是不同的是这道题并没有要求走完全部城市,因此不需要对经过的城市状态压缩,但是又增加了一个限制——票,每张票只能用一次,所以需要储存可以用的票(或者用过的票),也就是说对票进行状态压缩。
状态的定义几乎是相同的——
dp[S][i]
d
p
[
S
]
[
i
]
表示旅行家在城市 i 时,剩余票(或者使用票)的状态为 S 时的最小花费。
状态转移方程式比较好理解:
dp[S⊕2i][u]=min(dp[S][v]+dist[v][u]/tic[i])|i∈S
d
p
[
S
⊕
2
i
]
[
u
]
=
min
(
d
p
[
S
]
[
v
]
+
d
i
s
t
[
v
]
[
u
]
/
t
i
c
[
i
]
)
|
i
∈
S
小技巧:因为
i∈S
i
∈
S
,所以
S⊕2i
S
⊕
2
i
也就是把元素i从S中取出来。
:−P
:
−
P
方程式的意思就是从有第 i 张和第 j 张票且在城市 v 的状态使用第 i 张票到达城市 u,也就是
S⊕2i
S
⊕
2
i
。
那么最终状态就是剩余任何票的情况下,到达城市 b,取最小值。
□代码□
尽量不要copy,可以结合代码理解 :)
/*Lucky_Glass*/
#include<cstdio>
#include<algorithm>
#include<vector>
#include<cstring>
using namespace std;
#define TIC 8
#define CTY 30
#define INF 1e18
int n_tic,n_cty,n_rod,Start,End;
int tic[TIC+5],dist[CTY+5][CTY+5];
double dp[(1<<TIC)+5][CTY+5];
void Reset()
{
memset(dist,-1,sizeof dist);
memset(tic,0,sizeof tic);
for(int i=0;i<(1<<TIC)+5;i++)
for(int j=0;j<CTY+5;j++)
dp[i][j]=INF;
}
int main()
{
//freopen("in.txt","r",stdin);
while(true)
{
Reset();
scanf("%d%d%d%d%d",&n_tic,&n_cty,&n_rod,&Start,&End);
if(!n_tic && !n_cty && !n_rod && !Start && !End)
break;
for(int i=0;i<n_tic;i++)
scanf("%d",&tic[i]);
for(int i=0;i<n_rod;i++)
{
int s,e,l;
scanf("%d%d%d",&s,&e,&l);
dist[s][e]=dist[e][s]=l;
}
dp[(1<<n_tic)-1][Start]=0;
double ans=INF;
for(int S=(1<<n_tic)-1;S>=0;S--)
for(int u=1;u<=n_cty;u++)
if(dp[S][u]!=INF)
for(int v=1;v<=n_cty;v++)
if(dist[u][v]!=-1)
for(int i=0;i<n_tic;i++)
if((1<<i)&S)
{
double add=(double)dist[u][v]/(double)tic[i];
int lS=S^(1<<i);
dp[lS][v]=min(dp[lS][v],dp[S][u]+add);
}
for(int i=0;i<(1<<n_tic);i++)
ans=min(ans,dp[i][End]);
if(ans!=INF)
printf("%.4f\n",ans);
else
puts("Impossible");
}
return 0;
}