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

127 篇文章 0 订阅
85 篇文章 2 订阅

问题分析

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

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

此时 vS,s=Sv,us 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;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值