旅行商问题 状压DP

旅行商问题描述

现在有一个旅行商,在一个国家做生意。这个国家有N(2 <= N <= 15)个城市,城市之间有单行道可以通行,每条路都有相应的路费(0 <= d(I,j) <= 1000)。现在旅行商要从0号城市出发,经过所有的城市,最后回到0号城市。要求所花的路费最小。保证这些道路能构成一个环(可以一笔画)。

问题简化

给出一张带权有向有环图,用邻接矩阵表示,d(i,j)表示ij两个节点之间边的权值,INF表示没有边。要求从0号节点出发,经过每一个节点后正好回到0号节点,问经过边的总权值最小是多少。

问题分析

看到这道题,你的第一反应是不是暴力呀?嗯,很正常当时我也是这么想的。那我们不妨就来思考一下怎么暴力?其实呀,暴力就是一个全排列问题。把除0号节点外的所有节点全排列一遍,然后排除那些根本走不通的方案,最后在剩下的方案中找最小的花费。

嗯。这种方案不是不行,时间复杂度是O((n-1)!),在这里n最大是15,那最坏情况下的计算次数是87,178,291,200。超出了int的表示范围。但是这样还是可以骗分的

进一步分析

其实这个问题是NP难问题。不能在多项式时间内进行求解。但是这种问题是可以用DP的思路来求解的。我们定义一个集合S,表示已经走过的节点的集合,定义集合V表示所有节点。再有v表示当前的节点。定义dp[S][v] 表示已走过节点为S,当前节点为v时,从v出发,经过~S的节点回到0号节点的最小花费。现在定义u表示下一步要走到的节点,。

可以得到递推式

状态压缩

我们发现,在dp这个数组中有一个维度是表示状态的集合。但我们又知道,数组的下标必须是整数,那怎么办呢?

我们发现,一个节点要么在,要么不再S集合中。那么n个节点就可以表示成:“在、不在、在、不在S集合中”。再利用计算机二进制存储的特性,我们就可以把这种状态压缩到一个整数当中。而且我们发现N <= 15,也就是说这个表示状态的整数最大不过,就是65535,保证内存hold住。这种方法叫做状态压缩。

状压DP

状压完了,就要开始DP了。DP最重要的条件是什么?最有子结构。这里的子结构一定是最优的(参见01背包的推导方法)。问题是怎么保证子结构呢?假设有两个压缩后的状态i,j,当时,i <= j。这就保证了一定能用循环求解。代码是这样的:

int dp[1 << MAX_N][MAX_N]; //DP数组

void solve() {
    //初始化
    for (int S = 0; S < 1 << n; ++S)
        for (int i = 0; i < n; ++i)
            dp[S][i] = INF;
    dp[(1 << n) - 1][0] = 0;

    //DP
    for (int S = (1 << n) - 2; S >= 0; S--) {
        for (int v = 0; v < n; ++v) {
            for (int u = 0; u < n; ++u) { //遍历u
                if (!(S >> u & 1)) //没有访问过
                    dp[S][v] = min(dp[S][v], dp[S | 1 << u][u] + d[v][u]);
            }
        }
    }

    cout << dp[0][0] << endl;
}

现在来解释一下。

我们先假设S确定,来解释一下里面的循环。v循环是标准的DP循环,也可以参考01背包的解释

u循环的意思是遍历当当前节点是v时,下一步的走法。这时候你可能会产生疑问,为什么没有判断v u之间有没有边。因为如果v u之间没有边,那v u之间的距离就是INF,是不会被min选中的

min判断的前面表示不选u节点作为下一步,后面表示往后走一步。其实就是找最小值。自己理解一下。

然后我们来理解一下S循环。你可能会产生疑问,为什么S要逆序。01背包逆序是要防止数据的覆盖,但是这本来就是二维数组,不存在这种问题。

  要解释这个问题,我们就要先回顾一下动态规划的基本思路,就是有小规模的问题合并成大规模的问题。那什么是小规模的问题?是吗?当然不是,如果的话,就是一个点也没有走,这应该是最后要求解的答案(回顾一下dp[S][v]的定义)。规模最小的问题应该是已经走过了其他所有的点,就差一步就回到0号节点的状态就是(1 << n) – 2。因为已知dp[V][0]=0,所以这种DP是可行的。

原文地址:https://www.cnblogs.com/Iuppiter/p/12208687.html

时间: 2025-01-13 14:57:12

旅行商问题 状压DP的相关文章

HDU 5067 Harry And Dig Machine(状压DP)(TSP问题)

题目地址:pid=5067">HDU 5067 经典的TSP旅行商问题模型. 状压DP. 先分别预处理出来每两个石子堆的距离.然后将题目转化成10个城市每一个城市至少经过一次的最短时间模型.然后简单的状压DP就可以. 代码例如以下: #include <iostream> #include <cstdio> #include <string> #include <cstring> #include <stdlib.h> #incl

状压dp,区间dp,矩阵快速幂

DP 首先先回忆一下dp,dp叫做记忆化搜索,是一种可以把暴力搜索中重复的部分重复利用,从而到达减小复杂度的目的.比如最应该熟悉的背包模型,如果你把选择的过程看成一步一步的,那么在这么多的搜索路径中一定有着很多很多的重复部分,dp就是一种把重复的部分加以利用的方法.相信大家都已经在以前的练习中已经明白了dp是什么样的思路了,接下来的两种dp会在大家已经了解经典的背包dp等模型下展开. 状态压缩dp: 首先先讲一个状压dp最最经典的模型,求哈密尔顿路径问题,也叫做旅行商问题:给你一张图,你要在每个

ZOJ3305Get Sauce 状压DP,

状压DP的题目留个纪念,首先题意一开始读错了,搞了好久,然后弄好了,觉得DFS可以,最后超时,修改了很久还是超时,没办法看了一下n的范围,然后觉得状压可以,但是没有直接推出来,就记忆化搜索了一下,可是一直错,莫名奇妙,然后没办法看了一下题解,发现了下面这个比较好的方法,然后按照这个方程去推,然后敲,也是WA了好多把,写的太搓了,没人家的清楚明了,唉~也算是给自己留个纪念,状压一直做的都不太好~唉~还好理解了, 参考了  http://blog.csdn.net/nash142857/articl

poj 2411 Mondriaan&#39;s Dream(状压DP)

Mondriaan's Dream Time Limit: 3000MS   Memory Limit: 65536K Total Submissions: 12232   Accepted: 7142 Description Squares and rectangles fascinated the famous Dutch painter Piet Mondriaan. One night, after producing the drawings in his 'toilet series

(状压dp)uva 10817 Headmaster&#39;s Headache

题目地址 1 #include <bits/stdc++.h> 2 typedef long long ll; 3 using namespace std; 4 const int MAX=1e5+5; 5 const int INF=1e9; 6 int s,m,n; 7 int cost[125]; 8 //char sta[MAX]; 9 string sta; 10 int able[125]; 11 int dp[125][1<<8][1<<8]; 12 in

HDU5816 Hearthstone(状压DP)

题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5816 Description Hearthstone is an online collectible card game from Blizzard Entertainment. Strategies and luck are the most important factors in this game. When you suffer a desperate situation an

HDU 4336 容斥原理 || 状压DP

状压DP :F(S)=Sum*F(S)+p(x1)*F(S^(1<<x1))+p(x2)*F(S^(1<<x2))...+1; F(S)表示取状态为S的牌的期望次数,Sum表示什么都不取得概率,p(x1)表示的是取x1的概率,最后要加一因为有又多拿了一次.整理一下就可以了. 1 #include <cstdio> 2 const int Maxn=23; 3 double F[1<<Maxn],p[Maxn]; 4 int n; 5 int main() 6

Travel(HDU 4284状压dp)

题意:给n个城市m条路的网图,pp在城市1有一定的钱,想游览这n个城市(包括1),到达一个城市要一定的花费,可以在城市工作赚钱,但前提有工作证(得到有一定的花费),没工作证不能在该城市工作,但可以走,一个城市只能工作一次,问pp是否能游览n个城市回到城市1. 分析:这个题想到杀怪(Survival(ZOJ 2297状压dp) 那个题,也是钱如果小于0就挂了,最后求剩余的最大钱数,先求出最短路和 Hie with the Pie(POJ 3311状压dp) 送披萨那个题相似. #include <

BZOJ 1087: [SCOI2005]互不侵犯King( 状压dp )

简单的状压dp... dp( x , h , s ) 表示当前第 x 行 , 用了 h 个 king , 当前行的状态为 s . 考虑转移 : dp( x , h , s ) = ∑ dp( x - 1 , h - cnt_1( s ) , s' ) ( s and s' 两行不冲突 , cnt_1( s ) 表示 s 状态用了多少个 king ) 我有各种预处理所以 code 的方程和这有点不一样 ------------------------------------------------