动态规划【8】之状态压缩DP

状态压缩DP将状态作为数组的一维,进行动态规划。

例题:luogu1433 吃奶酪

房间里放着 n n 块奶酪。一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在 ( 0 , 0 ) (0,0) 点处。
输出要跑的最少距离。
其中 n 15 n \leq 15

因为要把所有的奶酪吃完,所以我们要考虑吃的顺序,不同的顺序对结果有很大的影响。从暴力的角度看,共有 n ! n! 个顺序,当 n = 15 n=15 时, n ! = 1307674368000 n!=1307674368000 ,显然会超时。

优化,考虑如下情况:
假设现在已经吃了 3 3 块奶酪,要去吃剩下的 n 3 n-3 块。已经吃的 3 3 块中只有最后一块,对剩下的 n 3 n-3 块的按什么顺序吃有影响。如前三块编号为 1 1 2 2 3 3 , 那么 123 123 (先吃第 1 1 块,再吃第 2 2 块,最后吃第 3 3 块), 213 213 对剩下 n 3 n-3 块按什么顺序吃是没有影响的。也就是说,我们对于已经吃过的奶酪,只关心吃了哪几块,和最后一块吃了哪一块。由此,可以减少状态数。

刚刚介绍的也是状态压缩的基本思想。现在开始介绍状态压缩,设 m a s k mask 表示 n n 位长度的二进制,第 i i 位(从右往左数, i i 0 0 开始)为 1 1 时表示吃了第 i i 块奶酪,为 0 0 时表示还没吃第 i i 块奶酪。

n = 7 n=7 , m a s k mask 在二进制表示下为 0000111 0000111 ,表示已经吃了第 0 0 块,第 1 1 块,第 2 2 块奶酪,没有吃第 3 3 块,第 4 4 块,第 5 5 块,第 6 6 块奶酪;

n = 7 n=7 , m a s k mask 在二进制表示下为 1000101 1000101 ,表示已经吃了第 0 0 块,第 2 2 块,第 6 6 块奶酪,没有吃第 1 1 块,第 3 3 块,第 4 4 块,第 5 5 块奶酪。

那么DP的状态 d p [ m a s k ] [ i ] dp[mask][i] 表示在 m a s k mask 状态,最后一块吃的是 i i 下的最小距离和。

输出吃了所有奶酪,最后一个吃的是 i i 下取一个最小值:
min { d p [ ( 1 < < n ) 1 ] [ i ] } , 0 i n 1 , \min\{dp[(1<<n)-1][i]\}, 0\leq i\leq n-1, 其中 ( 1 < < n ) 1 (1<<n)-1 二进制下的表示 n n 1 1 ,即吃了所有奶酪。

转移式:
d p [ m a s k ] [ i ] = min { d p [ m a s k ( 1 < < i ) ] [ j ] + d i s ( i , j ) } , dp[mask][i] = \min\{dp[mask-(1<<i)][j]+dis(i,j)\},
其中 d i s ( i , j ) dis(i,j) 表示下标为 i i j j 的距离; m a s k mask 要包含 i i j j 两个状态,即 ( m a s k & ( 1 < < i ) ) > 0 (mask\&(1<<i))>0 and ( m a s k & ( 1 < < j ) ) > 0 (mask\&(1<<j))>0 .

扫描二维码关注公众号,回复: 9918599 查看本文章

初始化:
d p [ 1 < < i ] [ i ] = x [ i ] x [ i ] + y [ i ] y [ i ] , 0 i n 1 dp[1<<i][i]=\sqrt{x[i]*x[i]+y[i]*y[i]},0\leq i\leq n-1 ;
其余点为无穷大。

代码:

/* ***********************************************
Author        : VFVrPQ
Created Time  : 三  3/ 4 14:21:47 2020
File Name     : luogu1433吃奶酪.cpp
Problem       : 
Description   : 
Solution      : 
Tag           : 
************************************************ */

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <string>
#include <cmath>
#include <cstdlib>
#include <vector>
#include <queue>
#include <stack>
#include <set>
#include <map>
#include <iomanip>
using namespace std;
#define DEBUG(x) cout<<x<<endl;
const int N = 17;
const int M = 1e9+7;
const int INF = 1e9+7;
const double eps = 1e-8;

int n;
double x[N], y[N];
double dp[1<<N][N];

double dis(double x1, double y1, double x2, double y2){
    return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}
int main()
{
    scanf("%d",&n);
    for (int i=0;i<n;i++){
        scanf("%lf%lf",&x[i],&y[i]);
    }
    //初始化
    int maxmask = 1<<n;
    for (int mask=0;mask<maxmask;mask++){
        for (int i=0;i<n;i++) dp[mask][i] = INF; // 初始化为极小值
    }
    for (int i=0;i<n;i++) dp[1<<i][i] = dis(0.0,0.0,x[i],y[i]);//走一步的
    for (int mask=0;mask<maxmask;mask++){
        for (int i=0;i<n;i++)if (mask&(1<<i)){
            for (int j=0;j<n;j++)if (mask&(1<<j)){
                if (fabs(dp[mask-(1<<i)][j]-INF)<eps) continue; // 
                dp[mask][i] = min(dp[mask][i], dp[mask-(1<<i)][j] + dis(x[i], y[i], x[j], y[j]));
            }
        }
    }
    double ans = INF;
    for (int i=0;i<n;i++) ans = min(ans, dp[maxmask-1][i]);
    printf("%.2lf\n", ans);
    return 0;
}
发布了74 篇原创文章 · 获赞 30 · 访问量 2万+

猜你喜欢

转载自blog.csdn.net/MustImproved/article/details/104654605