P1433 吃奶酪

题目描述

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

输入格式

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

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

输出格式

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

输入输出样例

输入 #1

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

输出 #1

7.41

说明/提示

数据规模与约定

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

提示

对于两个点(x1​,y1​),(x2​,y2​),两点之间的距离公式为(x1​−x2​)^2+(y1​−y2​)^2​。


2022.7.132022.7.13:新增加一组 HackHack 数据。


思路

状压dp

代码


/*用数组F[i][j]表示老鼠走到第i块奶酪,其他走过的奶酪坐标的二进制状态为j时的最短距离 (走过用1表示,没走过用0表示)
用循环来分别枚举所有可能的二进制状态、当前所在位置和在当前状态下可以到达的点
假设此时的二进制状态为s,起点为j,终点为i,用a[i][j]表示i到j之间的距离,F[j][k-(2^(i-1))]为当前位置在j 点且没有到达过i点的最短路径。

最终得状态转移方程f[i][k] = min(f[i][k], f[j][k-(2^(i-1)] + a[i][j]);*/
#include <bits/stdc++.h>//万能头文件
using namespace std;
#define min(a,b) (((a)<(b))?(a):(b)) //三目运算符,不过min函数是c++已经封装好的,因此这行可不用写
double a[20][20];从第i块到第j块的距离,使用两点之间距离公式(x1​−x2​)^2+(y1​−y2​)^2​ 
double x[20],y[20];//每块奶酪的横、纵坐标
double F[18][34000];//状压DP数组 在第i个点上,走过的二进制状态的十进制表达为j时,最短的距离 
int N; 
double dis(int v,int w){//计算第v,w个奶酪之间的距离
    return sqrt((x[v]-x[w]) * (x[v]-x[w]) + (y[v]-y[w]) * (y[v]-y[w]));//两点间距离公式 (x1​−x2​)^2+(跟上一行)(y1​−y2​)^2​

}
int main(){
    int i, j, k;
    double ans;
    memset(F, 127, sizeof(F));
    ans = F[0][0];
    scanf("%d", &N);
    for(i = 1; i <= N; i++){
        scanf("%lf%lf", &x[i], &y[i]);
    }
    x[0] = 0;
    y[0] = 0;
    for(i = 0; i <= N; i++){
        for(j = i + 1; j <= N; j++){
            a[i][j] = dis(i, j);//初始化
            a[j][i] = a[i][j];
        } 
    }
    for(i = 1; i <= N; i++){//初始化
        F[i][(1 << (i - 1))] = a[0][i];//在i点上且只有经过i点时距离是原点到i点的距离
    }
    for(k = 1; k < (1 << N); k++){//枚举所有二进制的状态
        for(i = 1; i <= N; i++){
            if((k & (1 << (i - 1))) == 0)
                continue;//i的位置没被走过,所以跳过
            for(j = 1; j <= N; j++){
                if(i == j){
                    continue;//同一个点直接跳过
                }
                if((k & (1 << (j - 1))) == 0){
                    continue;//j的位置未走过
                }    
                F[i][k] = min(F[i][k], F[j][k - (1 << (i - 1))] + a[i][j]);//递推式
            } 
        } 
    } 
    for(i = 1; i <= N; i++){
        ans = min(ans, F[i][(1 << N) - 1]);
    }
    printf("%.2f\n", ans);//保留2位小数
    return 0;
}

  • 11
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值