P1433 吃奶酪
吃奶酪
题目描述
房间里放着 \(n\) 块奶酪。一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在 \((0,0)\) 点处。
输入格式
第一行有一个整数,表示奶酪的数量 \(n\)。
第 \(2\) 到第 \((n + 1)\) 行,每行两个实数,第 \((i + 1)\) 行的实数分别表示第 \(i\) 块奶酪的横纵坐标 \(x_i, y_i\)。
输出格式
输出一行一个实数,表示要跑的最少距离,保留 \(2\) 位小数。
样例 #1
样例输入 #1
4
1 1
1 -1
-1 1
-1 -1
样例输出 #1
7.41
提示
数据规模与约定
对于全部的测试点,保证 \(1\leq n\leq 15\),\(|x_i|, |y_i| \leq 200\),小数点后最多有 \(3\) 位数字。
提示
对于两个点 \((x_1,y_1)\),\((x_2, y_2)\),两点之间的距离公式为 \(\sqrt{(x_1-x_2)^2+(y_1-y_2)^2}\)。
典型的状态压缩DP问题(很像是枚举子集,因为各个点之间的先后关系都不确定)
用二进制来表示每个奶酪是否吃过的状态eg.1000101
代表 1、3、7吃过
然后就考虑状态转移 f[i][k]:k状态下 当前在第i个奶酪处:
则考虑k状态时除i外其他点对i的转移:
f[i][k]=min{f[j][k-(1<<(i-1))]+dis(i,j)}
即 在j点状态中还未包含i点时
代码实现时 枚举状态k:0~1<<n -1 i:1~n j:1~n(i!=j)
注意 j不是1~i-1而是1~n 因为点之间的顺序未定!
对于枚举i j时也要判断i j 是否在状态k中 若否则continue
点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n;
double x[16],y[16];
double f[16][1024*64+5];
double dis(int i,int j)
{
return sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
}
int main()
{
ios::sync_with_stdio(false);
cin>>n;
for(int i=1;i<=n;i++)cin>>x[i]>>y[i];
x[0]=y[0]=0.0;
memset(f,127,sizeof(f));
for(int i=1;i<=n;i++)f[i][1<<(i-1)]=dis(0,i);
for(int k=0;k<=(1<<n)-1;k++)//二进制状态
{
for(int i=1;i<=n;i++)
{
if(!(k&(1<<(i-1))))continue;
for(int j=1;j<=n;j++)
{
if(i==j)continue;
if(!(k&(1<<(j-1))))continue;
f[i][k]=min(f[i][k],f[j][k-(1<<(i-1))]+dis(i,j));
}
}
}
double ans=INT_MAX;
for(int i=1;i<=n;i++)ans=min(ans,f[i][(1<<n)-1]);
printf("%.2lf\n",ans);
return 0;
}
此生无悔入OI 来生AK IOI

浙公网安备 33010602011771号