状态压缩动态规划(状压DP)

一个整数可以转化成二进制数,它可以代表某个集合的一个状态,这两者一一对应。
比如:
状态压缩动态规划(状压DP)_第1张图片

使用场景

如果一个题目适合用dp求解,但是状态很多(含有0、1元素的集合),可以把状态压缩为二进制数,使用状态压缩DP。整数的二进制表示状态,通过位运算进行状态转换。

例题

旅行商问题:
给定一系列城市和每对城市之间的距离,求解访问每一座城市一次并回到起始城市的最短回路。我们假设商人从0位置出发,最后依然回到位置0。
【思路】
使用dp[i][S]表示当前处于i地,遍历状态为S;
转移方程:
dp[j][S | (1 << j)] = min(dp[j][S | (1 << j)], dp[i][S] + dist[i][j]);(i->j的转换过程)
因为 (S|(1<= S ,所以 按照普通S从小到大顺序一定能正确遍历所有情况,如果所有城市可以被访问多次,则不能用该方法。

#include 
using namespace std;
int main()
{
    int n = 4;
    vector<vector<int>> dist(n, vector<int>(n, 0x3f3f3f3f));
    int M = (1 << n) - 1;
    vector<vector<int>> dp(n, vector<int>(M + 1, 0x3f3f3f3f));
    vector<vector<int>> roads = { { 0, 1, 10 }, { 1, 3, 25 }, { 3, 2, 30 },
        { 0, 2, 15 }, { 1, 2, 35 }, { 0, 3, 1 } };
    for (int i = 0; i < roads.size(); i++) {
        dist[roads[i][0]][roads[i][1]] = roads[i][2];
        dist[roads[i][1]][roads[i][0]] = roads[i][2];
    }
    dp[0][1] = 0; // dp[i][S]表示处在i城,遍历状态S
    for (int S = 1; S < M; S++) {
        for (int i = 0; i < n; i++) {
            if (!(S & (1 << i))) // i不在状态S内
                continue;
            for (int j = 0; j < n; j++) {
                if (S & 1 << j)
                    continue; // j已经在状态S内
                int next = S | (1 << j);
                if (next != M)
                    dp[j][next] = min(dp[j][next], dp[i][S] + dist[i][j]); //从i到j
                else if (dist[j][0] != 0x3f3f3f3f)
                    dp[j][next] = min(dp[j][next],
                        dp[i][S] + dist[i][j] + dist[j][0]); // 0x3f3f3f3f * 3 会溢出
            }
        }
    }
    int res = INT_MAX;
    for (int i = 0; i < n; i++) {
        res = min(res, dp[i][M]);
    }
    cout << res;
    getchar();
    return 0;
}

你可能感兴趣的:(数据结构和算法,算法题)