题目链接:https://www.luogu.com.cn/problem/P1433

题目大意

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

输入格式

第一行一个正整数 \(n\)。

接下来每行 \(2\) 个实数,表示第 \(i\) 块奶酪的坐标。

两点之间的距离公式为 \(\sqrt{(x_1-x_2)^2+(y_1-y_2)^2}\)。

输出格式

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

解题思路

定义状态 \(f[st][i]\) 表示当前状态为 \(st\) ,且最后一个到达的点是 \(i\) 点时的最少距离。

首先,因为 \(st\) 的二进制表示中的那些为 \(1\) 的位表示的是小老鼠已经到达的点,所以如果 \(st\) 的第 \(i\) 位不为 \(1\),则状态 \(f[st][i]\) 不合法。

其次:

如果状态 \(st\) 有且只有一位为 \(1\) (即 __builtin_popcount(st) == 1),并且我们假设为 \(1\) 的这一位为第 \(i\) 位,则 \(f[st][i] = \sqrt{x_i^2 + y_i^2}\) (因为小老鼠一开始在 \((0,0)\) 点,从 \((0,0)\) 点到 \((x_i,y_i)\) 点的距离是 \(\sqrt{x_i^2 + y_i^2}\));

否则(状态 \(st\) 为 \(1\) 的位数 \(\gt 1\)),说明状态 \(f[st][i]\) 是可以通过一个合法的状态 \(f[st2][j]\) 转换过来的。(其中 st2 = st^(1<<i)

此时,我们可以得到状态转移方程为:

\[f[st][i] = \min(f[st2][j] + \sqrt{(x_i-x_j)^2+(y_i-y_j)^2})
\]

其中,st2 = st^(1<<i)

而 \(\sqrt{(x_i-x_j)^2+(y_i-y_j)^2}\) 表示的就是点 \((x_j,y_j)\) 到点 \((x_i,y_i)\) 的距离。

实现代码如下:

#include <bits/stdc++.h>
using namespace std;
double dis(double x1, double y1, double x2, double y2) {
return sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
}
int n;
double x[15], y[15], f[(1<<15)][15];
bool vis[(1<<15)][15];
int main() {
cin >> n;
for (int i = 0; i < n; i ++) cin >> x[i] >> y[i];
for (int st = 0; st < (1<<n); st ++) {
for (int i = 0; i < n; i ++) {
if (!(st & (1<<i))) continue;
if (__builtin_popcount(st) == 1) f[st][i] = dis(0, 0, x[i], y[i]);
else {
int st2 = st ^ (1<<i);
for (int j = 0; j < n; j ++) {
if (!(st2 & (1<<j))) continue;
double tmp = f[st2][j] + dis(x[i], y[i], x[j], y[j]);
if (!vis[st][i] || f[st][i] > tmp) {
vis[st][i] = true;
f[st][i] = tmp;
}
}
}
}
}
double ans = f[(1<<n)-1][0];
for (int i = 1; i < n; i ++) ans = min(ans, f[(1<<n)-1][i]);
printf("%.2lf\n", ans);
return 0;
}

代码分析

我们对这个代码中的主要片段进行一下分析:

double dis(double x1, double y1, double x2, double y2) {
return sqrt((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2));
}

dis函数用于计算点 \((x_1,y_1)\) 到点 \((x_2,y_2)\) 之间的距离。

int n;
double x[15], y[15], f[(1<<15)][15];
bool vis[(1<<15)][15];

n用来表示点(或者说——奶酪)的数量。

\(x[i],y[i]\) 用于表示点的距离。

\(f[st][i]\) 的含义我们已经讲过了,这里就不再继续讲了。

\(vis[st][i]\) 相当于我们记忆化的操作。

我们以往的操作都会选择将 \(f[st][i]\) 赋为一家很大的值,或者将它赋值为-1来表示无穷大,但是我们开一个vis数组,通过 \(vis[st][i]\) 是否为 \(true\) 来判断状态 \(f[st][i]\) 有没有更新过也是可以的(没有更新过说明 \(f[st][i]\) 对应的状态还是无穷大,更新过说明 \(f[st][i]\) 已经被更新为了一个较小的值)。

这部分逻辑在我们代码中 \(f[st2][j]\) 更新 \(f[st][i]\) 的时候有遇到:

double tmp = f[st2][j] + dis(x[i], y[i], x[j], y[j]);
if (!vis[st][i] || f[st][i] > tmp) {
vis[st][i] = true;
f[st][i] = tmp;
}
if (__builtin_popcount(st) == 1) f[st][i] = dis(0, 0, x[i], y[i]);

这句话对应我们上面分析的第一种情况(如果状态 \(st\) 有且只有一位为 \(1\)),此时就直接更新 \(f[st][i]\) 为起点(\((0,0)\)) 到点 \(i\)(\((x_i, y_i)\)) 的距离即可。

否则,对于状态 \(f[st][i]\) ,需要找到所有它的前一步的状态 \(f[st2][j]\),并且通过如下代码求得 \(f[st][i]\):

int st2 = st ^ (1<<i);
for (int j = 0; j < n; j ++) {
if (!(st2 & (1<<j))) continue;
double tmp = f[st2][j] + dis(x[i], y[i], x[j], y[j]);
if (!vis[st][i] || f[st][i] > tmp) {
vis[st][i] = true;
f[st][i] = tmp;
}
}

而最终的状态 \(st\) 肯定等于 \(2^n-1\)(\(2^n-1\) 的后 \(n\) 位都为 \(1\),表示 \(n\) 个点都走过),所以答案即为

\[\min_{i \in [0,n-1]} f[2^n-1][i]
\]

我们是通过如下代码段来获得答案的:

double ans = f[(1<<n)-1][0];
for (int i = 1; i < n; i ++) ans = min(ans, f[(1<<n)-1][i]);
printf("%.2lf\n", ans);

最后,也不要忘了输出我们的 ans,同时保留2位小数哦。

最后的最后:

关于数位DP,最好还是按照坐标从 \(0\) 到 \(n-1\) 为好,因为这样的 \(i\) 刚好能跟状态在 \([0, 2^n-1]\) 范围内的数字一一对应。所以希望还是能够按照坐标从 \(0\) 开始比较好。

洛谷P1433 吃奶酪 题解 状态压缩DP的更多相关文章

  1. 洛谷 P1433 吃奶酪 Label:dfs && 剪枝Ex

    题目描述 房间里放着n块奶酪.一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在(0,0)点处. 输入输出格式 输入格式: 第一行一个数n (n<=15) 接下来每行2个实数,表示第i块 ...

  2. 洛谷 P1433 吃奶酪【DFS】+剪枝

    题目链接:https://www.luogu.org/problemnew/show/P1433 题目描述 房间里放着n块奶酪.一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在(0,0)点处 ...

  3. 洛谷 P1433 吃奶酪 状压DP

    题目描述 分析 比较简单的状压DP 我们设\(f[i][j]\)为当前的状态为\(i\)且当前所在的位置为\(j\)时走过的最小距离 因为老鼠的坐标为\((0,0)\),所以我们要预处理出\(f[1& ...

  4. 集训作业 洛谷P1433 吃奶酪

    嗯?这题竟然是个绿题. 这个题真的不难,不要被他的难度吓到,我们只是不会计算2点之间的距离,他还给出了公式,这个就有点…… 我们直接套公式去求出需要的值,然后普通的搜索就可以了. 这个题我用的深搜,因 ...

  5. 洛谷P1433 吃奶酪【dfs】【剪枝】

    题目:https://www.luogu.org/problemnew/show/P1433 题意: 给定n个坐标,要求从(0,0)开始走遍所有点,最少经过的路程. 思路: 刚开始想像数字三角形一样适 ...

  6. 洛谷 P1433 吃奶酪

    题目描述 房间里放着n块奶酪.一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在(0,0)点处. 输入输出格式 输入格式: 第一行一个数n (n<=15) 接下来每行2个实数,表示第i块 ...

  7. 洛谷 - P1433 - 吃奶酪 - dfs

    https://www.luogu.org/problemnew/show/P1433 并不是每一个求最短距离就是bfs,这个肯定是dfs. 直接计算15!可以知道枚举必定超时,但是! 我们dfs非常 ...

  8. 洛谷 P1433 吃奶酪(记忆化)

    题目描述 房间里放着n块奶酪.一只小老鼠要把它们都吃掉,问至少要跑多少距离?老鼠一开始在(0,0)点处. 输入输出格式 输入格式: 第一行一个数n (n<=15) 接下来每行2个实数,表示第i块 ...

  9. 洛谷P1433 吃奶酪

    #include<iostream> #include<math.h> using namespace std ; ; int n; bool st[N]; double x[ ...

随机推荐

  1. 【Android】在程序中使用触力反馈

    触力反馈又名:hapticFeedbackEnabled 一般有两种实现方式 第一种是在XML布局文件里面设置 android:hapticFeedbackEnabled="true&quo ...

  2. 图解IDEA中配置Maven并创建Maven的Web工程

    打开IDEA,File->Settings,如下图所示: 2.在Settings中按照如下进行配置,如下图所示:

  3. 留学生想要搞定Reading List?只需这三步即可

    听到有同学在抱怨“一本书都读不完,还怎么搞定reading list啊?”别急,小编这就来给你支招啦!你的文献阅读方法错了,读起来不仅效率低,而且无法做到熟练运用.因此,你需要以下这3步,就能搞定文献 ...

  4. Vue-cli3与springboot项目整合打包

    一.需求        使用前后端分离编写了个小程序,前端使用的是vue-cli3创建的项目,后端使用的是springboot创建的项目,部署的时候一起打包部署,本文对一些细节部分进行了说明.   二 ...

  5. HTML设置表格

    1. 设置表格内容对齐方式 在HTML中通常通过align设置对齐方式,文字是: text-align ,表格是:align 如果将align属性设置给<table>标签,只能改变< ...

  6. 攻防世界--web新手练习区(1)

      1. 题目描述:X老师想让小明同学查看一个网页的源代码,但小明却发现鼠标右键不管用了.  http://111.198.29.45:53629 通过阅读题目描述分析,我们需要查看源码,但是鼠标右键 ...

  7. 吴裕雄 Bootstrap 前端框架开发——Bootstrap 辅助类:内容居中

    <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title> ...

  8. 第1节 IMPALA:8、shell交互窗口使用;9、外部和内部shell参数

    impala当中的元数据的同步的问题impala当中创建的数据库表,直接就可以看得到,不用刷新hive当中创建的数据库表,需要刷新元数据才能够看得到 因为impala的catalog的服务,我们需要通 ...

  9. XV6源代码阅读-文件系统

    Exercise1 源代码阅读 文件系统部分 buf.h fcntl.h stat.h fs.h file.h ide.c bio.c log.c fs.c file.c sysfile.c exec ...

  10. 2. react 简书 头部(header) 图标添加

    1. 访问 iconfont 并注册 登陆 2. 进入 iconfont 头部 图标管理->我的项目 3. 点击右边的文件夹 + 号 图标 创建我的项目 输入项目名称即可 4.在 搜索框 搜索 ...