POJ 3411 Paid Roads 状态压缩DP(旅行商问题)

一、题目大意

有m条单向边连接了N个城市(1<=m,N<=10),走这些路径是要付费的,有两种付费方式,例如从 城市 ai 到 bi

1、在城市 ci 提前支付,(ci 可能等于 ai,可能不等于)

2、在到达 bi 后支付

第一种支付的金额为 P,第二种支付的金额为R。(P<=R)

求出从 1 到 N的最小花费。

二、解题思路

定义DP数组,dp[S][v]代表已经走过的点为S,且当前在 v 的最小花费。

初始化整个数组为 无穷,dp[1][0]=0,代表已经走过 0号点,且在0号点的花费为0。

然后循环 大小为 1、2 ... N - 1 大小的集合。

执行如下递推式。

dp[S | 1 << u][u] = min(dp[S | 1 << u][u] , dp[S][v] + costVU)

其中costVU等于 v 到 u 需要计算,初值是 达到 u 之后付费的金额。

然后循环集合S 以内的点,挨个计算从这个点 提起支付 v 到 u 路径的金额,最后取一个最小。

注意事项:本题目是可以走回头路的,因为集合相同,但是终点不同。

例如测试用例中,从1到达2,再从2回到1,再从1到达3,是1到3的最便宜的路径(在2位置支付1到3的路径的金额)。

所以已走过的点可以再走。

另外,因为 从1达到 2,从2回到1,会更新 dp[0b11][0]的值,而按照集合大小循环,计算到dp[0b11][1]时,dp[0b11][0]的值已经循环过了,所以每个集合的大小要循环两次。

三、代码

#include 
using namespace std;
const int MAX_N = 10, INF = 0x3f3f3f3f;
int dp[1 << MAX_N][MAX_N], cost[MAX_N][MAX_N], preCost[MAX_N][MAX_N][MAX_N], N, all;
void handle(int S, int v, int u)
{
    if (dp[S][v] == INF)
    {
        return;
    }
    int costVU = cost[v][u];
    for (int i = 0; i < N; i++)
    {
        if (S >> i & 1)
        {
            costVU = min(costVU, preCost[i][v][u]);
        }
    }
    if (costVU == INF)
    {
        return;
    }
    if (dp[S][v] + costVU < dp[S | 1 << u][u])
    {
        dp[S | 1 << u][u] = dp[S][v] + costVU;
    }
}
void solveItem(int size)
{
    int used = 0;
    for (int i = 0; i < size; i++)
    {
        used = used | 1 << i;
    }
    while (used < all)
    {
        for (int v = 0; v < N; v++)
        {
            for (int u = 0; u < N; u++)
            {
                if (used >> v & 1)
                {
                    handle(used, v, u);
                }
            }
        }
        int x = used & -used;
        int y = used & ~(used + x);
        used = used + x + (y / x / 2);
    }
}
void doDp()
{
    for (int i = 0; i < (1 << MAX_N); i++)
    {
        for (int j = 0; j < MAX_N; j++)
        {
            dp[i][j] = INF;
        }
    }
    all = (1 << N) - 1;
    dp[1][0] = 0;
    for (int i = 1; i <= N - 1; i++)
    {
        solveItem(i);
        solveItem(i);
    }
    int ans = INF;
    for (int i = 1; i < (1 << N); i++)
    {
        ans = min(ans, dp[i][N - 1]);
    }
    if (ans == INF)
    {
        printf("impossible\n");
    }
    else
    {
        printf("%d\n", ans);
    }
}
void input()
{
    for (int i = 0; i < MAX_N; i++)
    {
        for (int j = 0; j < MAX_N; j++)
        {
            cost[i][j] = INF;
            for (int k = 0; k < MAX_N; k++)
            {
                preCost[i][j][k] = INF;
            }
        }
    }
    for (int i = 0; i < MAX_N; i++)
    {
        cost[i][i] = 0;
        for (int j = 0; j < MAX_N; j++)
        {
            preCost[i][j][j] = 0;
        }
    }
    int m, a, b, c, P, R;
    scanf("%d%d", &N, &m);
    for (int i = 0; i < m; i++)
    {
        scanf("%d%d%d%d%d", &a, &b, &c, &P, &R);
        a--;
        b--;
        c--;
        preCost[c][a][b] = min(preCost[c][a][b], P);
        cost[a][b] = min(cost[a][b], R);
    }
}
int main()
{
    input();
    doDp();
    return 0;
}

你可能感兴趣的:(动态规划,算法)