動態規劃【8】之狀態壓縮DP

狀態壓縮DP將狀態作爲數組的一維,進行動態規劃。

例題:luogu1433 喫奶酪

房間裏放着 nn 塊奶酪。一隻小老鼠要把它們都喫掉,問至少要跑多少距離?老鼠一開始在 (0,0)(0,0) 點處。
輸出要跑的最少距離。
其中n15n \leq 15

因爲要把所有的奶酪喫完,所以我們要考慮喫的順序,不同的順序對結果有很大的影響。從暴力的角度看,共有n!n!個順序,當n=15n=15時,n!=1307674368000n!=1307674368000,顯然會超時。

優化,考慮如下情況:
假設現在已經吃了33塊奶酪,要去喫剩下的n3n-3塊。已經喫的33塊中只有最後一塊,對剩下的n3n-3塊的按什麼順序喫有影響。如前三塊編號爲112233, 那麼123123(先喫第11塊,再喫第22塊,最後喫第33塊),213213對剩下n3n-3塊按什麼順序喫是沒有影響的。也就是說,我們對於已經喫過的奶酪,只關心吃了哪幾塊,和最後一塊吃了哪一塊。由此,可以減少狀態數。

剛剛介紹的也是狀態壓縮的基本思想。現在開始介紹狀態壓縮,設maskmask表示nn位長度的二進制,第ii位(從右往左數,ii00開始)爲11時表示吃了第ii塊奶酪,爲00時表示還沒喫第ii塊奶酪。

n=7n=7,maskmask在二進制表示下爲00001110000111,表示已經吃了第00塊,第11塊,第22塊奶酪,沒有喫第33塊,第44塊,第55塊,第66塊奶酪;

n=7n=7,maskmask在二進制表示下爲10001011000101,表示已經吃了第00塊,第22塊,第66塊奶酪,沒有喫第11塊,第33塊,第44塊,第55塊奶酪。

那麼DP的狀態dp[mask][i]dp[mask][i]表示在maskmask狀態,最後一塊喫的是ii下的最小距離和。

輸出吃了所有奶酪,最後一個喫的是ii下取一個最小值:
min{dp[(1<<n)1][i]},0in1,\min\{dp[(1<<n)-1][i]\}, 0\leq i\leq n-1,其中(1<<n)1(1<<n)-1二進制下的表示nn11,即吃了所有奶酪。

轉移式:
dp[mask][i]=min{dp[mask(1<<i)][j]+dis(i,j)},dp[mask][i] = \min\{dp[mask-(1<<i)][j]+dis(i,j)\},
其中dis(i,j)dis(i,j)表示下標爲iijj的距離;maskmask要包含iijj兩個狀態,即(mask&(1<<i))>0(mask\&(1<<i))>0 and (mask&(1<<j))>0(mask\&(1<<j))>0.

初始化:
dp[1<<i][i]=x[i]x[i]+y[i]y[i],0in1dp[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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章