前言:《算法竞赛入门经典》中将最优匹配问题归在复杂状态的动态规划下,第一次看的时候完全不懂…,隔几天后再看顿时豁然开朗,快点写下笔记和总结。在此问题之前还有树上的动态规划,以后再总结一下。
问题描述:空间里有n个点P0,P1,…,Pn-1,你的任务是把它们配成n/2对(n是偶数),使得每个点恰好在一个点对中。所有点对中两点的距离之和应尽量小。其中 n≤20。
要匹配的点最多才20个,所以这里可以把所有点看成一个点集,并且可以用二进制表示方法来表示这个集合。(学的时候感觉,太强了,太好用了。但需要先明白位运算)。
如:01101 表示第 0,2,3个元素存在于集合中,相当于全集的子集表示。由于二进制为从0开始,所以上述的点编号也从0开始。
用二进制表示的好处:便于寻找那些点在集合中,便于划分子集!
思路:要求的是所有点的最优匹配,我们把大问题划分为小问题,再根据小问题所求 的解来求解大问题;
先求解任意一个只含有两个点的子集,求这些子集的最优解(即求解 点 i 到
全集S中其他点的距离的最小值);
再求解任意一个只含有四个点的子集,因为只含有两个点的子集已经计算过。
所以我们在原来计算过的基础上,在S中寻找其他的两个点,计算它们的距离。
再将这四个点并成一个集合,同样地要取最小解。
(即若原来0,1两点已经匹配好,那我们要求的是,所有包含0和1且含有四个点
的集合的最优解)
这样依次类推,直到求得全集的最优解 。
(我们每次求得的解都是,含有某些配对的集合的最优解)
定义状态:d[s] 表示 点集s的最优匹配,则状态方程表示为:
d[s] = min( d[s] , dist(i,j)+d[s^(i<< i )^(1<< j )];
其中s^(1<< i)^(1<< j) 表示 集合s除去 i和j 的子集;
相当于一种构造集合的方式:通过往集合中添加一对匹配的点构造集合(一开始为空集)。
下面是通过紫书学习的代码,注释的都是我学习时一点点弄懂的;原书上代码有一点小错误,有读者在看原书的话注意一下。
#include<iostream>
#include<cmath>
using namespace std;
const int N =