由旅行商问题认识何为状态压缩

动态规划

动态规划(dynamic programming)是运筹学的一个分支,是求解决策过程(decision process)最优化的数学方法。20世纪50年代初美国数学家R.E.Bellman等人在研究多阶段决策过程(multistep decision process)的优化问题时,提出了著名的最优化原理(principle of optimality),把多阶段过程转化为一系列单阶段问题,利用各阶段之间的关系,逐个求解,创立了解决这类过程优化问题的新方法——动态规划。

https://blog.csdn.net/hebtu666/article/category/8018091基础总结

 

状态压缩

我们在进行动态规划时,有时状态相当复杂,看上去需要很多空间,比如一个数组才能表示一个状态,那么就需要对状态进行某种编码,进行压缩表示。

比如:状态和某个集合有关,集合里可以有一些元素,没有另一些元素,那么就可以用一个整数表示该集合,每个元素对应于一个bit,有该元素,则该bit就是1。

这个皇后问题就可以帮助理解https://blog.csdn.net/hebtu666/article/details/84631083

 

旅行商问题

 

旅行商问题(TravelingSalesmanProblem,TSP)是一个经典的组合优化问题。经典的TSP可以描述为:一个商品推销员要去若干个城市推销商品,该推销员从一个城市出发,需要经过所有城市后,回到出发地。应如何选择行进路线,以使总的行程最短。从图论的角度来看,该问题实质是在一个带权完全无向图中,找一个权值最小的Hamilton回路。由于该问题的可行解是所有顶点的全排列,随着顶点数的增加,会产生组合爆炸,它是一个NP完全问题。由于其在交通运输、电路板线路设计以及物流配送等领域内有着广泛的应用,国内外学者对其进行了大量的研究。早期的研究者使用精确算法求解该问题,常用的方法包括:分枝定界法、线性规划法、动态规划法等。但是,随着问题规模的增大,精确算法将变得无能为力,因此,在后来的研究中,国内外学者重点使用近似算法或启发式算法,主要有遗传算法模拟退火法蚁群算法禁忌搜索算法、贪婪算法神经网络等。

 

如图,我们如何从0点出发,以最小代价走过所有的点?

 

分析

 

抛开这个图,我们试想:我们可能从0点一次性走到哪些点呢?

只可能1点、2点、3点、4点(废话)

我们如果知道了

1)从1点开始走,经过所有的点,最后走到了0点的最小距离;a

2)从2点开始走,经过所有的点,最后走到了0点的最小距离;b

3)从3点开始走,经过所有的点,最后走到了0点的最小距离;c

4)从4点开始走,经过所有的点,最后走到了0点的最小距离;d

请再次注意:

我们可能从0点一次性走到哪些点呢?

只可能1点、2点、3点、4点(废话)

所以我们可以从0点走到1点,再从1点经过最小距离走到0点。

2点、3点、4点同理。

那么我们取min(a+3,b+MAX,c+4,d+MAX)即可。

3为0点走到1点的距离,4为0点走到3点的距离。

MAX代表此路不通。。

 

那我们继续思考,abcd怎么求呢?其实同样的:

 

比如:求a

从1点开始走,经过所有的点,最后走到了0点的最小距离;a

同样的思考:我们可能从1点一次性走到哪些点呢?

答:可以走到0点、2点、3点、4点。

注意:由于问题要求为:只能经过每一个顶点一次,所以我们要去掉0点,因为我们经过0点,最后再到0点就会重复,所以,

“经过所有的点”是不准确的,是除0点的所有的点。

所以只有三个点可能从1点走过去,我们需要求:

1)从2点开始走,经过除0点所有的点,最后走到了0点的最小距离x

2)从3点开始走,经过除0点所有的点,最后走到了0点的最小距离y

3)从4点开始走,经过除0点所有的点,最后走到了0点的最小距离z

然后取min(x+到2点的距离,y+到3点的距离,z+到4点的距离)

 

归纳表达式

1)我们会发现,每一次状态转移其实对应的是一个点的集合:

以后还会有经过除1,2点,经过除1,2,3点等等。

 

2)还有从哪个点开始走:

这两个条件就可以描述当前的状态。

我们把上面的总结用公式表示出来:

S为已经访问过的点的集合

v表示当前所在点。

那么dp[S][v]就表示为从点v出发访问剩余点,最终返回0点的最小长度。

我们把刚才的求解过程用公式表达出来。

dp[S][v]=min(dp[S\bigcup {u}][u]+d(v,u)\mid u\euro S)

∉这个符号我实在没找到,理解意思。

 

压缩

像这样,状态可以根据集合表示的DP,我们称作状态压缩DP。

状态和某个集合有关,集合里可以有一些元素,没有另一些元素,那么就可以用一个整数表示该集合,每个元素对应于一个bit,有该元素,则该bit就是1。 

本题来说,所有点都在集合里就是11111

哪个点没在,对应的位就为0

 

注意:对于状态不是整数而是集合的情况,通常不太容易确定DP的顺序,如果确实不好想顺序,可以通过记忆化搜索来做。

集合S,如果对于任意S(i)<S(j),那么一定有i<j。所以确定了顺序。

 

确定dp表的大小,有n个城市,从0开始编号,那么dp表的行数就是n,列数就是2^(n-1)

int n;
int d[MAX_N][MAX_N];//邻接矩阵
int dp[1<<MAX_N][MAX_N];

对于数字x,要看它的第i位是不是1,那么可以通过 (x >>i) & 1取出那一位来看

先用足够大的值初始化dp数组,不要影响结果,然后核心代码:

for(int S = (1<<n)-2 ; S >= 0 ; s--)//每一个集合
{
    for(int v = 0 ; v < n ; v++)//每一个出发点
    {
        for(int u = 0 ; u < n ; u++)//访问每一个点
        {
            if(!(S>>u&1))//判断是否在集合中
            {
                dp[S][v] = min(dp[S][v] , dp[S | 1<<u][u]+d[v][u]);
            }
        }
    }
}

 

总结

 

当我们把状态压缩应用到动态规划中,可以用来精简状态,节约空间,也方便转移。

最常见的就是用二进制来表是状态,利用各种位移运算,就可以实现O(1)的转移。

 

兔兔RabbitMQ!!! CSDN认证博客专家 Go/GoLang Redis MySQL
本人在腾讯总部工作,csdn博客专家,acm退役菜鸡,leetcode刷题狂,c++/Java/python/go/php各种后端语言都喜欢玩,安卓、node、叶子、jsp等前端也能写,最喜欢鼓捣各种组件(虽然没人用hhhh),各位请多指教。
已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 书香水墨 设计师:CSDN官方博客 返回首页