状态压缩dp之TSP
(2011-09-02 18:56:29)
转自:http://blog.sina.com.cn/s/blog_6383bcba0100wbko.html
一个n个点的带权的有向图,求一条路径,使得这条路经过每个点恰好一次,并且路径上边的权值和最小(或者最大)。
或者求一条具有这样性质的回路,这是经典的TSP问题。
n <= 16 (重要条件,状态压缩的标志)
状态?
转移?
如何表示一个点集:
由于只有16个点,所以我们用一个整数表示一个点集:
例如:
所以一个整数i就表示了一个点集;
整数i可以表示一个点集,也可以表示是第i个点。
状态表示:dp[i][j]表示经过点集i中的点恰好一次,不经过其它的点,并且以j点为终点的路径,权值和的最小值,如果这个状态不存在,就是无穷大。
状态转移:
最后的结果是:
技巧:利用2进制,使得一个整数表示一个点集,这样集合的操作可以用位运算来实现。例如从集合i中去掉点j:
#include <cstdio>
#include <queue>
#include <iostream>
using namespace std;
#define ffor(i, n) for(i=0; i<n; ++i)
#define max(a, b) (a>b ? a:b)
#define min(a, b) (a<b ? a:b)
const int inf = 0x7fffffff;
const int n = 4;
int weg[4][4]={
{0, 2, inf, 3},
{inf, 0, 6, 1},
{inf, 2, 0, 4},
{5, 1, 3, 0}
};
struct info{ int s, t; }; // s表示当前集合,t表示集合最后一个元素
queue<info> q; // bfs队列
int d[1<<n][n];
void dp()
{
int i, j;
int x;
info tmp1, tmp;
ffor(i, 1<<n) ffor(j, n) d[i][j]=inf; // 一开始只初始化到(1<<n)-1, 失败
ffor(i, n){
x=1<<i; d[x][i]=0;
tmp.t=i; tmp.s=x; q.push(tmp);
}
while(!q.empty()){
tmp=q.front(); q.pop();
ffor(j, n){ // 可以用邻接表存储,加速
x=1<<j;
// x已经在当前集合,加入失败,或者当前集合的最后元素与x没有边,continue
if((tmp.s)&x || weg[tmp.t][j]==inf) continue;
tmp1.s=(tmp.s)|x; tmp1.t=j;
d[tmp1.s][j]=min(d[tmp.s][tmp.t]+weg[tmp.t][j], d[tmp1.s][j]); // dp, 不解释
q.push(tmp1);
}
}
}
int main()
{
int i, ans=inf;
dp();
ffor(i, n) ans=min(ans, d[(1<<n)-1][i]);
printf("%d\n", ans);
return 0;
}
588

被折叠的 条评论
为什么被折叠?



