最优配对问题:
空间里有n个点P0,P1,…,Pn-1,你的任务是把它们配成n/2对(n是偶数),使得每个点恰好在一个点对中。
所有点对中两点的距离之和应尽量小。
dp方程:
d[i][S] 点0~i 的最优匹配,S为状态集合。
d[i][S] = min(d[i][j],dist(i,j)+d[i-1][S-{i}-{j}]);
集合 S 和 j 是否有交集 (S&(1<<j)) ;
除去 i j 的集合怎么表示呢? d[i-1][S^(1<<i)^(1<<j)];
for(int i=0; i<n; i++){
for(int S=0; S<(1<<n); S++){
d[i][S] = INF;
for(int j=0; j<i; j++)
d[i][S] = min(d[i][S],dist[i][j]+d[i-1][S^(1<<i)^(1<<j)]);
}
}
-----------------------------------------------------------------------------------------------------------------------
i 一定是S中最大的元素,那么dp就可以减少一维
d(S) = min(|PiPj| + d(S-{i}-{j})) | i = max(S);
for(int S=0; S<(1<<n); S++){
int i,j;
d[S] = INF;
for(int i=0; i<n; i++)
if(S&(1<<i)) break;
for(int j=i+1; j<n; j++)
if(S&(1<<j)) d[S] = min(d[S],dist[i][j]+d[S^(1<<i)^(1<<j)]);
}
注意,在上述的程序中求出的i是S中的最小元素,而不是最大元素,但这并不影响答案
另外, j的枚举只需从i+1开始-- 既然i是S中的最小元素,则说明其他元素都比i大
S的枚举顺序:如果S’ 是S的真子集,则一定有S‘<S,因此若以S递增的顺序计算,需要用到某个d值时,它一定已经计算出来了
*************************************************************************************************************************
货郎担问题(TSP) N<=15 城市编号0~n-1
有n个城市,用1,2,…,n表示,城i,j之间的距离为L[i][j],有一个货郎从一个城市出发到其他城市一次且仅一次,最后回到起点的路线,
怎样选择行走路线使总路程最短?
是NPC难题,,规模小可以用dp求解
首先可以注意到可以直接规定起点和终点为城市0,然后设d(i,S)表示当前在城市i,还需访问集合S中的城市各一次后回到城市0的最短长度
状态转移: d(i,S) = min{ d(j,S-{j}) + dist(i,j)|j∈S }
边界为: d{i,{}} = dist(0,i);
最终答案: d(0,{1,2,3,....,n-1});
时间复杂度为O(n^2*2^n);
**************************************************************************************************************************
图的色数:
图论有一个经典问题是这样的:给一个无向图G,把图中的结点染成尽量少的颜色,是的相邻结点颜色不同
设d(S)表示把结点集S染色,所需要颜色数的最小值,则d(S) = d(S-S’)+1, 其中S’是S的子集,并且内部没有边(即不存在S‘内的两个结点u和v使得u和v相邻)
换句话说,S’是一个"可以染成同一颜色" 的结点集
首先通过预处理保存每个结点集是否可以染成同一颜色(即“内部没有边”) , 则算法的主要时间取决于“高效的枚举一个集合S的所有子集
d[0] = 0;
for(int S=1; S<(1<<n); S++){
d[S] = INF;
for(int s0=S; s0; s0=(s0-1)&S){
if(no_edges_inside[s0])
d[S] = min(d[S],d[S-s0]+1);
}
}
end~