洛谷 P1433 吃奶酪

原题icon-default.png?t=N7T8https://www.luogu.com.cn/problem/P1433

Description

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

Input

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

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

Output

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

Sample 1

InputOutput
4
1 1
1 -1
-1 1
-1 -1
7.41

Hint

数据规模与约定

对于全部的测试点,保证 1≤n≤15,∣xi​∣,∣yi​∣≤200,小数点后最多有 2 位数字。

提示

解题思路

状态dp: 适用范围 N≤21

状态压缩的思想是用二进制来表示状态。用一个整数的二进制形式的每一个二进制位 0 或 1 表示一个状态。

在这里表示n块奶酪,就代表有n位二进制,每一位2进制代表一块奶酪。

例:100110 代表第2、3、6块奶酪已经吃到。

        111111 代表所有奶酪吃到。

状态转移方程:Fi,k=min⁡(Fi,k,Fj,k−2i−1+Ai,j)Fi,k​=min(Fi,k​,Fj,k−2i−1​+Ai,j​)

Fj,k−2i−1​ 为在 j 点且没有走过 i 点的最短距离, Ai,j​ 是从 i 到 j 的距离。

AC代码

状态dp

#include <iostream>
using namespace std;
#include <cstring>
#include <cmath>
#include <algorithm>
int n;
double a[16][16],x[16],y[16],f[16][33000];
//计算距离
double distance(int v,int w){
	return sqrt((x[v]-x[w])*(x[v]-x[w])+(y[v]-y[w])*(y[v]-y[w]));
}
int main(){
	cin>>n;
	x[0]=y[0]=0;
	for(int i=1;i<=n;i++) cin>>x[i]>>y[i];
	memset(f,127,sizeof(f));
    //计算每块奶酪直接的距离
	for(int i=0;i<=n;i++){
		for(int j=i+1;j<=n;j++) a[i][j]=a[j][i]=distance(i,j);
	}
    //初始化每块奶酪的距离
	for(int i=1;i<=n;i++) f[i][1<<(i-1)]=a[0][i];
	for(int k=1;k<(1<<n);k++){
		for(int i=1;i<=n;i++){
			if((k&(1<<(i-1)))==0) continue;    //没走过的路跳过
			for(int j=1;j<=n;j++){
				if(i==j) continue;            //相等直接跳过
				if((k&(1<<(j-1)))==0) continue; //没走过的路跳过
				f[i][k]=min(f[i][k],f[j][k-(1<<(i-1))]+a[i][j]); 
			}
		}
	}
	double ans=f[0][0];
	for(int i=1;i<=n;i++) ans=min(ans,f[i][(1<<n)-1]);
	printf("%.2lf",ans); 
}

dfs(超时)

#include<iostream>
#include <cmath>
#include <algorithm>
using namespace std;
int n,flag[20];
double x[20],y[20],a[20][20];
bool vis[20];
double Min=0x3ffffffffffff;
void dfs(int k,double d){
	if(d>Min) return ;
	if(k>n){
		Min=d;
		return;
	}
	for(int i=1;i<=n;i++){
		if(vis[i]==0){
			vis[i]=1;
			flag[k]=i;
			dfs(k+1,d+a[flag[k-1]][flag[k]]);
			vis[i]=0;
		}
	}
} 
int main(){
	cin>>n;
	x[0]=y[0]=0;
	for(int i=1;i<=n;i++) {
		cin>>x[i]>>y[i];
	}
	for(int i=0;i<=n;i++){
		for(int j=0;j<=n;j++){
			a[i][j]=sqrt((x[i]-x[j])*(x[i]-x[j])+(y[i]-y[j])*(y[i]-y[j]));
		}
	} 
	dfs(1,0.0);

	printf("%.2lf\n",Min);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

j2189259313

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值