状态压缩DP将状态作为数组的一维,进行动态规划。
房间里放着 块奶酪。一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在 点处。
输出要跑的最少距离。
其中 。
因为要把所有的奶酪吃完,所以我们要考虑吃的顺序,不同的顺序对结果有很大的影响。从暴力的角度看,共有 个顺序,当 时, ,显然会超时。
优化,考虑如下情况:
假设现在已经吃了
块奶酪,要去吃剩下的
块。已经吃的
块中只有最后一块,对剩下的
块的按什么顺序吃有影响。如前三块编号为
,
,
, 那么
(先吃第
块,再吃第
块,最后吃第
块),
对剩下
块按什么顺序吃是没有影响的。也就是说,我们对于已经吃过的奶酪,只关心吃了哪几块,和最后一块吃了哪一块。由此,可以减少状态数。
刚刚介绍的也是状态压缩的基本思想。现在开始介绍状态压缩,设 表示 位长度的二进制,第 位(从右往左数, 从 开始)为 时表示吃了第 块奶酪,为 时表示还没吃第 块奶酪。
如 , 在二进制表示下为 ,表示已经吃了第 块,第 块,第 块奶酪,没有吃第 块,第 块,第 块,第 块奶酪;
如 , 在二进制表示下为 ,表示已经吃了第 块,第 块,第 块奶酪,没有吃第 块,第 块,第 块,第 块奶酪。
那么DP的状态 表示在 状态,最后一块吃的是 下的最小距离和。
输出吃了所有奶酪,最后一个吃的是
下取一个最小值:
其中
二进制下的表示
个
,即吃了所有奶酪。
转移式:
其中
表示下标为
和
的距离;
要包含
和
两个状态,即
and
.
初始化:
;
其余点为无穷大。
代码:
/* ***********************************************
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;
}