openjudge 海贼王之伟大航路(状压dp)

问题分析

我们去考虑如何设计一个状态,才能满足无后效性和最优子结构呢?
如果我们从一维去想,用 d p [ v ] 表示终点为 v 时花费的最小时间是多少,那么明显是不够的,因为它无法保证你是否访问节点的时候会重复,然鹅题目要求恰好每一个节点只访问一次。所以,我们去加一维集合状态去约束它。用 d p [ S ] [ v ] 来表示恰好已经无重复访问节点集合 S 并且终点为 v 时所花费的最小时间,这样我们就可以满足题意了。
转移方程也显然易见

d p [ S ] [ v ] = m i n { d p [ s ] [ u ] + e [ u ] [ v ] }

此时 v S , s = S v , u s

#include<bits/stdc++.h>
using namespace std;

int n,e[20][20],dp[1<<20][20],inf = 1e9;

int rec(int s,int v) 
{
    if (dp[s][v] != inf)
        return dp[s][v];
    if (s != 0 && v == 0) //如果已经访问过其它节点但是终点却为0了
        return dp[s][v] = inf;
    if (s == 0 && v == 0) //边界条件, 已经访问过的集合为0, 并且在起点, 此时不花费时间
        return dp[s][v] = 0;
    int res = inf;
    for (int u = 0; u < n; ++u)
        if (u != v && (s >> u & 1)) //如果u属于s集合
            res = min(res, rec(s - (1 << u), u) + e[u][v]);//状态[已经访问过集合为{s - (1 << u)}, 终点为u]所需的最少时间+u->v的时间
    return dp[s][v] = res; //构成已访问集合为s终点为v的状态
}

int main()
{
    //freopen("in.txt","r",stdin);
    cin>>n;
    for(int i = 0; i < n; ++i){
        for(int j = 0; j < n; ++j){
            cin>>e[i][j];
        }
    }
    /*初始到达目标并且访问完所有集合所需代价无穷大*/
    for(int i = 1; i < 1<<n; ++i)
        for(int j = 0; j < n; ++j)
            dp[i][j] = inf;
    printf("%d\n",rec((1<<(n-1))-1,n-1));//从起点0开始到到达终点n-1并且恰好不重复访问完所有的集合所需的最少时间
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Eternally831143/article/details/81267253
今日推荐