LuoguP1433 吃奶酪

题目描述

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

输入格式

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

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

输出格式

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

样例#1

输入:         输出:7.41          
4           
1 1
1 -1
-1 1
-1 -1

数据规模

对于全部的测试点,保证\(1\leq n\leq15;|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}\)

思路:

首先这个题第一眼看上去就是个搜索,然后噼里啪啦一顿打完以后发现过不了(T了一个点)。

然后就考虑有没有别的更高效的办法。一般来说,DP能做的题搜索都可以做,所以我就考虑能不能用DP来解决这个题。

实际上是可以的。我们可以考虑用状压DP来解决。

\(f[i][j]\)为当前走到\(i\)这个点,走过\(j\)这个集合中的点后所走的最短路径(\(i\)包括在\(j\)中)。

怎么转移呢?很明显我们需要枚举每种可能的\(j\),然后再枚举当前\(j\)中所有可能的\(i\),然后再枚举\(i\)这个点是从哪里走过来的,就可以完成转移。

具体思路就这些,还有需要注意的就是此题输入的\(x_i\)\(y_i\)可能出现小数。

Code:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
int n;
double x[20], y[20];
double f[20][65536];
double dis[20][20];
inline double Length(double dx1,double dy1,double dx2,double dy2){
    return std::sqrt((dx1 - dx2) * (dx1 - dx2) + (dy1 - dy2) * (dy1 - dy2));
}
inline double _min(double x, double y) { return x < y ? x : y; }
int main(){
    scanf("%d", &n);
    for (int i = 1; i <= n;++i)
        scanf("%lf%lf", &x[i], &y[i]);
    for (int i = 1; i <= n;++i){
        for (int j = 1; j <= n;++j){
            if(i==j) dis[i][j] = 0;
            else dis[i][j] = Length(x[i], y[i], x[j], y[j]);
        }
    }
    memset(f, 127, sizeof(f));
    for (int i = 1; i < (1 << n);++i){
        for (int j = 1; j <= n;++j){
            if(((i>>(j-1))&1)==0) continue;//判断当前点是否在集合中
            if(i==(1<<(j-1))){//判断当前集合中是否只有这一个点
                f[j][i] = 0;
                continue;
            }
            for (int k = 1; k <= n;++k){
                if(((i>>(k-1))&1)==0) continue;//判断当前点是否在集合中
                if(j==k) continue;
                f[j][i] = _min(f[j][i], f[k][i - (1 << (j - 1))] + dis[k][j]);//转移,f[k][i - (1 << (j - 1))]表示的就是走到k时的最短距离
            }
        }
    }
    double res = -12;
    for (int i = 1; i <= n;++i){
        double ot = f[i][(1 << n) - 1] + Length(0, 0, x[i], y[i]);//不要忘了加上到(0,0)点的距离
        if(res==-12||res>ot) res = ot;
    }
    printf("%.2lf\n", res);
    return 0;
}
posted @ 2020-07-22 10:40  Shadow_hyc  阅读(122)  评论(0)    收藏  举报