最短Hamilton路径(位运算基本思路)

题目描述

给定一张 n 个点的带权无向图,点从 0~n-1 标号,求起点 0 到终点 n-1 的最短Hamilton路径。 Hamilton路径的定义是从 0 到 n-1 不重不漏地经过每个点恰好一次。

输入格式

第一行输入整数n。
接下来n行每 n个整数,其中第 i 行第 j 个整数表示点 i 到 j 的距离(记为a[i,j])。
对于任意的x,y,z,数据保证 a[x,x]=0,a[x,y]=a[y,x] 并且 a[x,y]+a[y,z]>=a[x,z]。

输出格式

输出一个整数,表示最短Hamilton路径的长度。

数据范围

1≤n≤20
0≤a[i,j]≤107

输入样例:

5
0 2 4 5 1
2 0 6 5 3
4 6 0 8 3
5 5 8 0 5
1 3 3 5 0

输出样例:

18

题解:思考:如何表示哪些点经过,哪些点没有经过,我们可以用一个n位的二进制数来表示该点的状态,若其第 i 为1,则表示第 i 个点已经被经过了,反之未被经过。我们还需知道当前的位置情况,因此我们可以用 F[ i ] [ j ]来表示“ 点被经过的状态” 对应的二进制数为 i ,且目前处于点 j 时的最短路。起点有 F[ 1 ] [ 0 ] = 0,即只经过了起点,且当前位于起点0,最短路为0. 为了方便起见,我们将数组初始化为最大值。

这里用到了状态压缩动态规划,公式为:
F[ i ] [ j ] = min(F[ i ] [ j ], F[ i - (1 << j)] [k] + weight[ k ] [ j ]);
详细解释在代码中…
代码如下:

#include
#include
#include
using namespace std;
typedef long long ll;
int f[1 << 20][20]; //f[i][j]表示i的状态,为1则代表经过,为0则代表没经过。j代表当前所处的位置。
int weight[20][20]; //存储边的权值
int main()
{
    int n;
    cin >> n;
    for(int i = 0; i < n; i++)
        for(int j = 0; j < n; j++)
            cin >> weight[i][j];
    memset(f, 0x3f, sizeof f);
    f[1][0] = 0;  //起点为0,i为1代表经过0这个起点,j为0代表当前在0这个位置
    for(int i = 0; i < 1 << n; i++)
        for(int j = 0 ; j < n; j++)
            if(i >> j & 1){ //如果经过了第j位,则与上一状态比较,寻找最小值
                for(int k = 0; k < n; k++)
                    if(i - (1 << j) >> k & 1)//表示上一状态没有经过第j位,经过了第k位的情况
                        f[i][j] = min(f[i][j],f[i - (1 << j)][k] + weight[k][j]);//取最小值
    cout << f[(1 << n) - 1][n - 1] << endl;
    return 0;
}

你可能感兴趣的:(数据结构与算法题解总集)