状态压缩DP将状态作为数组的一维,进行动态规划。
房间里放着 n n n 块奶酪。一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在 ( 0 , 0 ) (0,0) (0,0) 点处。
输出要跑的最少距离。
其中 n ≤ 15 n \leq 15 n≤15。
因为要把所有的奶酪吃完,所以我们要考虑吃的顺序,不同的顺序对结果有很大的影响。从暴力的角度看,共有 n ! n! n!个顺序,当 n = 15 n=15 n=15时, n ! = 1307674368000 n!=1307674368000 n!=1307674368000,显然会超时。
优化,考虑如下情况:
假设现在已经吃了
3
3
3块奶酪,要去吃剩下的
n
−
3
n-3
n−3块。已经吃的
3
3
3块中只有最后一块,对剩下的
n
−
3
n-3
n−3块的按什么顺序吃有影响。如前三块编号为
1
1
1,
2
2
2,
3
3
3, 那么
123
123
123(先吃第
1
1
1块,再吃第
2
2
2块,最后吃第
3
3
3块),
213
213
213对剩下
n
−
3
n-3
n−3块按什么顺序吃是没有影响的。也就是说,我们对于已经吃过的奶酪,只关心吃了哪几块,和最后一块吃了哪一块。由此,可以减少状态数。
刚刚介绍的也是状态压缩的基本思想。现在开始介绍状态压缩,设 m a s k mask mask表示 n n n位长度的二进制,第 i i i位(从右往左数, i i i从 0 0 0开始)为 1 1 1时表示吃了第 i i i块奶酪,为 0 0 0时表示还没吃第 i i i块奶酪。
如 n = 7 n=7 n=7, m a s k mask mask在二进制表示下为 0000111 0000111 0000111,表示已经吃了第 0 0 0块,第 1 1 1块,第 2 2 2块奶酪,没有吃第 3 3 3块,第 4 4 4块,第 5 5 5块,第 6 6 6块奶酪;
如 n = 7 n=7 n=7, m a s k mask mask在二进制表示下为 1000101 1000101 1000101,表示已经吃了第 0 0 0块,第 2 2 2块,第 6 6 6块奶酪,没有吃第 1 1 1块,第 3 3 3块,第 4 4 4块,第 5 5 5块奶酪。
那么DP的状态 d p [ m a s k ] [ i ] dp[mask][i] dp[mask][i]表示在 m a s k mask mask状态,最后一块吃的是 i i 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,
min{dp[(1<<n)−1][i]},0≤i≤n−1,其中
(
1
<
<
n
)
−
1
(1<<n)-1
(1<<n)−1二进制下的表示
n
n
n个
1
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)\},
dp[mask][i]=min{dp[mask−(1<<i)][j]+dis(i,j)},
其中
d
i
s
(
i
,
j
)
dis(i,j)
dis(i,j)表示下标为
i
i
i和
j
j
j的距离;
m
a
s
k
mask
mask要包含
i
i
i和
j
j
j两个状态,即
(
m
a
s
k
&
(
1
<
<
i
)
)
>
0
(mask\&(1<<i))>0
(mask&(1<<i))>0 and
(
m
a
s
k
&
(
1
<
<
j
)
)
>
0
(mask\&(1<<j))>0
(mask&(1<<j))>0.
初始化:
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
dp[1<<i][i]=x[i]∗x[i]+y[i]∗y[i],0≤i≤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;
}