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;
} 
posted @ 2023-01-03 15:54  PKU_IMCOMING  阅读(40)  评论(0)    收藏  举报