• P1433 吃奶酪——dfs+状态压缩剪枝


    吃奶酪

    题目描述

    房间里放着 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
    • 2
    • 3
    • 4
    • 5

    样例输出 #1

    7.41
    
    • 1

    提示

    数据规模与约定

    对于全部的测试点,保证 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 数据。

    分析

    1. 此题的思路不难,但是想做对挺不容易的,一看n的大小15,就可以想到用全排列,暴力逐个判断走完每个点不同排列方式,找最短距离,但是如果仅仅一个简单的最优性剪枝if (dis >= ans) return;这是不够的,会有一个点TLE;需要用到状态压缩剪枝;
    2. 为什么需要状态压缩? 由于到达某个点的状态有多种情况,比如到达12,可能之前走过1 2 3,也可能走过4 5 6,所以记录状态时,可以用状态压缩,用二进制序列的十进制数来表示每个状态,用一个数组f[][],一维就是点的编号,二维就是到达该点是的状态(已走过的点组成二进制序列的十进制数);
    3. f数组存了到达不同点所经过的状态,以及他的当前最优值,如果下次换一个状态还到达该点,发现新的状态所用距离不如上一次,就直接continue(不是return,不能顺手直接return,刚开始return卡了我一天找不到错误,可能还是不够理解),当前 if (f[i][newStatus] != 0 && f[i][newStatus] <= f[lastid][status] + calDis(last, {xx, yy}))不满足,只能说明第i个点新的状态已比较优,不用再通过上一个状态连当前的点,可以去判断下一个点,也就是i++,就不能直接return走;
    4. 由于从起点0,0出发,dfs的参数就不能仅仅用lastid去获取上个点的信息(还需要加一个last存上一个点的坐标),比如(0,0)出发,搜第一个点,然后利用i=0,和lastid=0,同时查点,但起始点(0,0)并不是坐标编号为0,就会错误计算;还需要注意坐标为实数,说明还x,y可能为小数,所以double存,不然下面第二轮测试点过不去,WA掉;
      在这里插入图片描述

    在这里插入图片描述

    #include 
    
    using namespace std;
    
    int n;
    double x, y;
    double ans = 10000000;
    vector<pair<double, double>> v;
    double vis[20];
    //状态记录 d[12][42501] 表示走到编号为12的点,所经过的点的编号为42501二进制序列为1的数
    double f[20][33000];
    
    //两个点的距离
    double calDis(pair<double, double> p1, pair<double, double> p2) {
        return sqrt((p1.first - p2.first) * (p1.first - p2.first) +
                    (p1.second - p2.second) * (p1.second - p2.second));
    }
    
    //lastid:上一个坐标的编号,last:上一个点的坐标,status为当前走过的点编号组成的二进制序列的十进制值
    void dfs(int u, int lastid, pair<double, double> last, int status, double dis) {
        //剪枝
        if (dis >= ans)
            return;
        if (u == n) {
            ans = min(ans, dis);
            return;
        }
        for (int i = 0; i < n; i++) {
            double xx = v[i].first, yy = v[i].second;
            if (!vis[i]) {
                //更新状态,因为加上了i这个点,status + 2^i(1左移i位就是2的i的值)
                int newStatus = status + (1 << i);
                //状态压缩剪枝
                if (f[i][newStatus] != 0 && f[i][newStatus] <= f[lastid][status] + calDis(last, {xx, yy}))
                    continue;
                f[i][newStatus] = f[lastid][status] + calDis(last, {xx, yy});
                vis[i] = 1;
                dfs(u + 1, i, {xx, yy}, newStatus, dis + calDis(last, {xx, yy}));
                vis[i] = 0;
            }
        }
    }
    
    int main() {
        cin >> n;
        for (int i = 0; i < n; i++) {
            cin >> x >> y;
            v.push_back({x, y});
        }
        dfs(0, 0, {0, 0}, 0, 0);
        printf("%.2lf", ans);
        return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
  • 相关阅读:
    竞赛选题 基于机器视觉的车道线检测
    Linux——网络配置
    Matlab图像处理-三基色
    使用python快速搭建接口自动化测试脚本实战总结
    Linux安装进程树状图显示工具pstree
    ES6——Set和Map集合介绍
    谈一下相对位置编码
    江西农业大学择校分析(附23招生简章)
    12.vue3组件化开发(非父子组件之间的通信和插槽)
    权限系统设计方案 RBAC模型
  • 原文地址:https://blog.csdn.net/weixin_51995229/article/details/127682241