POJ 2686 Traveling by Stagecoach 状态压缩DP

一、题目大意

给我们一个无向图,起点和终点,要求我们计算出从起点到达终点的路径。

题目限制我们最多只能走 N 步,每次走一步消耗一张票,但可以享受到这张票带来的加速,例如第1步是从1走到3且使用第2张票,则消耗的时间为 d[1][3]/T[2]。

(1<=N<=8,每条边长度不超过100)

二、解题思路

我们定义DP数组,dp[S][v]代表我们使用的的票的集合为S且到达v的最短时间。起初的时候给整个数组填满无穷大(1e7就够了),对起点A做初始化 dp[0][A]=0,然后让S从空集开始主键向全集循环,循环内部再循环所有的票i、路径端点v和u。

如果S包含第 i 张票,则更新dp[S][v] = min(dp[S][v] , dp[ S去掉 i 的差集 ][u] + d[u][v] / T[i] )

min里的第二个参数,代表我们使用一些票,走到第 u 的位置,之后再使用第 i 张票从 u 走到 v 的距离。

最终循环结束后,找出各种用票方式,到达终点的最小值mint。

如果 mint > (票数 * 100)代表走不到终点,输出“不可能”。

否则输出 mint即可(%.3f)

三、代码

#include <iostream>
using namespace std;
const int MAX_N = 8;
const int MAX_M = 30;
const double INF = 10000000.0;
const int INF_INT = 10000000;
double dp[1 << MAX_N][MAX_M];
int T[MAX_N];
int d[MAX_M][MAX_M];
int N, M, P, A, B;
void solve()
{
    fill(dp[0], dp[0] + (1 << MAX_N) * MAX_M, INF);
    dp[0][A] = 0.0;
    for (int S = 0; S < (1 << N); S++)
    {
        for (int i = 0; i < N; i++)
        {
            for (int v = 0; v < M; v++)
            {
                for (int u = 0; u < M; u++)
                {
                    if (S >> i & 1)
                    {
                        dp[S][v] = min(dp[S][v], dp[S & ~(1 << i)][u] + (d[u][v] * 1.0) / T[i]);
                    }
                }
            }
        }
    }
}
void putAns()
{
    double mint = INF;
    for (int S = 0; S < (1 << N); S++)
    {
        mint = min(mint, dp[S][B]);
    }
    if (mint > 1000.0)
    {
        printf("Impossible\n");
    }
    else
    {
        printf("%.3f\n", mint);
    }
}
int main()
{
    int x, y, z;
    while (true)
    {
        scanf("%d%d%d%d%d", &N, &M, &P, &A, &B);
        if (N == 0 && M == 0 && P == 0 && A == 0 && B == 0)
        {
            break;
        }
        --A, --B;
        for (int i = 0; i < N; i++)
        {
            scanf("%d", &T[i]);
        }
        fill(d[0], d[0] + (MAX_M * MAX_M), INF_INT);
        for (int i = 0; i < M; i++)
        {
            d[i][i] = 0;
        }
        for (int i = 0; i < P; i++)
        {
            scanf("%d%d%d", &x, &y, &z);
            d[--x][--y] = z;
            d[y][x] = z;
        }
        solve();
        putAns();
    }
    return 0;
}

四、相关参考文献

《挑战程序设计竞赛(第二版)》P191 - P195

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值