P1433 吃奶酪 —(状压DP)

文章目录

  • 一、题目
  • 吃奶酪
    • 题目描述
    • 输入格式
    • 输出格式
    • 样例 #1
      • 样例输入 #1
      • 样例输出 #1
    • 提示
        • 数据规模与约定
        • 提示
  • 二、题解
      • 1 dfs+剪枝
      • 状压dp


一、题目

吃奶酪

题目描述

房间里放着 n n n 块奶酪。一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在 ( 0 , 0 ) (0,0) (0,0) 点处。

输入格式

第一行有一个整数,表示奶酪的数量 n n n

2 2 2 到第 ( n + 1 ) (n + 1) (n+1) 行,每行两个实数,第 ( i + 1 ) (i + 1) (i+1) 行的实数分别表示第 i i i 块奶酪的横纵坐标 x i , y i x_i, y_i xi,yi

输出格式

输出一行一个实数,表示要跑的最少距离,保留 2 2 2 位小数。

样例 #1

样例输入 #1

4
1 1
1 -1
-1 1
-1 -1

样例输出 #1

7.41

提示

数据规模与约定

对于全部的测试点,保证 1 ≤ n ≤ 15 1\leq n\leq 15 1n15 ∣ x i ∣ , ∣ y i ∣ ≤ 200 |x_i|, |y_i| \leq 200 xi,yi200,小数点后最多有 3 3 3 位数字。

提示

对于两个点 ( x 1 , y 1 ) (x_1,y_1) (x1,y1) ( x 2 , y 2 ) (x_2, y_2) (x2,y2),两点之间的距离公式为 ( x 1 − x 2 ) 2 + ( y 1 − y 2 ) 2 \sqrt{(x_1-x_2)^2+(y_1-y_2)^2} (x1x2)2+(y1y2)2


2022.7.13 2022.7.13 2022.7.13:新增加一组 Hack \text{Hack} Hack 数据。

二、题解

  • 基本思路:刚开始想到是dfs来解决这道题,从(0,0)点开始搜索,吃完n块奶酪更新最少距离,但是有几个点TLE,只得了90分

1 dfs+剪枝

//90fen
#include
using namespace std;
#define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
#define endl "\n"
#define int long long
typedef pair<int,int> PII; 
int n,num=0;
double a[16][3],ans=1<<30;
bool vis[16];//记录访没访问过 
//两点间距离 
double get_dist(double x1, double y1, double x2, double y2){
	return sqrt(pow(x1-x2,2)+pow(y1-y2,2));
}
//dfs 
void dfs(double x, double y, double dist){
	if(dist>ans) return;//剪枝 
	if(num>=n){//吃完n块奶酪了! 
		ans=min(ans,dist); 
		return; 
	}
	//cout<
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			dist+=get_dist(x,y,a[i][1],a[i][2]);
			vis[i]=true;
			num++;
			dfs(a[i][1],a[i][2],dist);
			dist-=get_dist(x,y,a[i][1],a[i][2]);//回溯 还原状态 
			vis[i]=false;
			num--;
		}
	}
	
}

void solve(){
	cin>>n;
	for(int i=1;i<=n;i++)
	  cin>>a[i][1]>>a[i][2];//记录两个点 
	dfs(0,0,0);//老鼠一开始在 (0,0) 点处
	printf("%.2lf",ans);
}

signed main(){
	//IOS;
	int T=1;
	while(T--){
		solve();
	}
	return 0;
}
/*
15
0 0
1 1
1 -1
-1 1
-1 -1
2 2
2 0
2 -2
0 -2
-2 -2
-2 0
-2 2
0 2
1 3
1 4

21.73
*/

状压dp

状态压缩动态规划,就是我们俗称的状压DP,是利用计算机二进制的性质来描述状态的一种DP方式

  • 我们发现n只最大有15,一共有2^n种状态,从000…0到111…1,0代表没有吃奶酪,1代表吃了奶酪,我们暴力枚举所有得状态。
  • 状态表示:f[i] [j]表示第i个状态,当前在第j个点,所需得最小代价。这里得第j个点,我是按输入奶酪坐标的的顺序编号的。
  • 最后吃完所有得奶酪,可能会在某个点,我们需要取所有点得最小值,即为最少距离。
const int N = 16, INF=1e18+10;
int n;
double a[N][3],f[1<<N][N];
//f[i][j]表示第i个状态(哪些吃了,哪些没吃),当前在第j个奶酪所花费的最小代价 

double get_dist(double x1,double y1,double x2,double y2){//求两点间距离
	return sqrt((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));
}

void solve(){
	cin>>n;
	for(int i=1;i<=n;i++)
	  cin>>a[i][1]>>a[i][2];
	memset(f,127,sizeof(f));
	f[0][0]=0;//起点 
	for(int i=0;i<1<<n;i++)//枚举状态 
	  for(int j=0;j<=n;j++)//枚举当前在第j个点 
	      for(int k=1;k<=n;k++)//决策:去第k个点 
	        if(!(i&(1<<(k-1))))//判断第k块奶酪还没被吃掉 
	           f[i+(1<<(k-1))][k]=min(f[i+(1<<(k-1))][k],f[i][j]+get_dist(a[j][1],a[j][2],a[k][1],a[k][2])); 
    double res=INF;
	for(int i=1;i<=n;i++)
	  res=min(res,f[(1<<n)-1][i]);
	printf("%.2lf",res);
}

你可能感兴趣的:(洛谷题单,算法,c++,状压dp)