【状压DP】 ◆POJ 2686◆ Traveling by Stagecoach

本文介绍了一种使用状态压缩动态规划解决旅行者问题的方法。该问题要求找出从城市A到城市B的最小花费路径,考虑到有限数量的不同类型的票,每张票对应不同的成本计算方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

◆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[S2i][u]=min(dp[S][v]+dist[v][u]/tic[i])|iS d p [ S ⊕ 2 i ] [ u ] = min ( d p [ S ] [ v ] + d i s t [ v ] [ u ] / t i c [ i ] ) | i ∈ S
小技巧:因为 iS i ∈ S ,所以 S2i S ⊕ 2 i 也就是把元素i从S中取出来。 :P : − P
方程式的意思就是从有第 i 张和第 j 张票且在城市 v 的状态使用第 i 张票到达城市 u,也就是 S2i 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;
}

The End

Thanks for reading!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值