动态规划方法解旅行商问题(TSP Traveling Salesperson Problem)
http://blog.csdn.net/hellonerd/article/details/50920234
首先是TSP的状态转移方程:
dp[A+vk][vk] = min ( dp[A][vj] + edge[vj][vk] )
A是已经经过了的节点的集合,vj是从起点开始经过A里面所有的节点后到达的最末
dp[A][vj]就储存了这段路径的距离
从没走过的节点里选一个加入路径,取最短
最后A集合包含所有节点的时候就是最短的距离了
状态压缩:
用0、1代表所有的的点是走过还是没走过(是否包含在A里面)
二进制的码(再也不要百度了。。。。)
#include <algorithm>
#include <cstring>
#include <iostream>
using namespace std;
#define INF 0x3f3f3f3f
const int n = 10;
int dp[ 1 << n ][ n ];
int edge[ 1 << n ][ n ];
void tsp () {
memset ( dp, INF, sizeof ( dp ) );
dp[ 1 ][ 0 ] = 0; //起点为0,起点的状态是1( 0位是1 )
// i,k不需要在经过起点了
for ( int i = 1; i < ( 1 << n ); ++i ) { // 2^n种状态
for ( int j = 0; j < n; ++j ) {
if ( dp[ i ][ j ] == INF )
continue;
//从i到k + 从k到j
for ( int k = 1; k < n; ++k ) {
// k已经走过了
if ( i & ( 1 << k ) )
continue;
// 记录新的状态i,添加了k位为1
int nex = ( i | ( 1 << k ) );
dp[ nex ][ k ] =
min ( dp[ nex ][ k ], dp[ i ][ j ] + edge[ j ][ k ] );
}
}
}
int ans = INF;
for ( int i = 0; i < n; ++i )
ans = min ( ans, dp[ ( 1 << n ) - 1 ][ i ] + edge[ i ][ 0 ] );
printf ( "%d\n", ans );
}
所以要用三进制
而且有多个起点
/**
hdu3001 状态压缩dp+三进制。
每个顶点经过最多2次,也就是说有0,1,2三总状态,我们状态转移的时候要用三进制。另外起点任意,
所以dp[bit[i]][i]=0,剩下的初始化为INF。状态转移方程为:dp[nex][k] = min(dp[nex][k], dp[i][j] +
edge[j][k]); 其中( next = i + bit[k])。
三进制没有对应的移位操作因此我们要模拟实现:首先把0~3^10之间的所有数都用一个数组ternary[i][j]表示出来,
1~10位分别代表i化为三进制后每个位上对应的值(第一位对应个位,第10位对应最高位),然后状态转移就可以了
*/
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
#define INF 0x3f3f3f3f
using namespace std;
int n, m;
int edge[ 12 ][ 12 ];
int dp[ 60000 ][ 12 ]; //当前状态哪些走过了,走到了的行数位置
int bit[ 11 ] = {1, 3, 9, 27, 81, 243, 729, 2187, 6561, 19683, 59049};
int ternary[ 60000 ][ 12 ]; //每个数的三进制
void make_trb () {
for ( int i = 0; i < bit[ 10 ]; ++i ) {
int b = i;
for ( int j = 0; j < 10; ++j ) {
ternary[ i ][ j ] = b % 3;
b /= 3;
}
}
}
int tsp () {
int ans = INF;
memset ( dp, INF, sizeof ( dp ) );
// dp[ 1 ][ 0 ] = 0; //起点为0,初始状态为2<<0 = 1
for ( int j = 0; j < n; ++j ) //每个节点都是初始状态
dp[ bit[ j ] ][ j ] = 0;
int flag;
// for ( int i = 1; i < ( 1 << n ); ++i ) { // 2^n种状态
for ( int i = 0; i < bit[ n ]; ++i ) {
flag = 1; //代表所有点都走过
for ( int j = 0; j < n; ++j ) {
//状态i的三进制第j位是0,还存在没有走过的点
if ( ternary[ i ][ j ] == 0 )
flag = 0;
//没有i -- j 的通路
if ( dp[ i ][ j ] == INF )
continue;
//从i到k + 从k到j
for ( int k = 0; k < n; ++k ) {
// k已经走过了,( 状态i下的第k位>1 )
// if ( i & ( 1 << k ) )
// k就是j,k走过两次了,边不存在
if ( j == k || ternary[ i ][ k ] >= 2 || edge[ k ][ j ] == -1 )
continue;
// 记录新的状态i,添加了k位为1
// int nex = ( i | ( 1 << k ) );
int nex = i + bit[ k ];
dp[ nex ][ k ] =
min ( dp[ nex ][ k ], dp[ i ][ j ] + edge[ j ][ k ] );
}
}
if ( flag ) {
for ( int j = 0; j < n; ++j )
ans = min ( ans, dp[ i ][ j ] );
}
}
return ans;
}
int main () {
make_trb ();
while ( ~scanf ( "%d%d", &n, &m ) ) {
memset ( edge, -1, sizeof ( edge ) );
for ( int i = 0; i < m; ++i ) {
int a, b, c;
scanf ( "%d%d%d", &a, &b, &c );
--a, --b;
if ( edge[ b ][ a ] == -1 )
edge[ b ][ a ] = edge[ a ][ b ] = c;
else
edge[ b ][ a ] = edge[ a ][ b ] = min ( edge[ a ][ b ], c );
}
int sol = tsp ();
if ( sol == INF )
sol = -1;
printf ( "%d\n", sol );
}
return 0;
}