P1433 吃奶酪 标签: 动态规划,dp | 状态压缩
详见:https://www.luogu.com.cn/problem/P1433
-
有可以简便记录存储勾股定理的方式,但是我要简便,就是直接传结构体的点,就用不了这个,或许可以用哈希,但是会搞得更麻烦
- 其实感觉更多的因素是懒~~~
就不写基础原理了,直接看注释吧
点击打开非map版
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
int all, n;
double ans = 200000000;
struct point {
double x, y;
} a[100];
double dp[1 << 15][20] = {};
int lowbit(int x) {
return x & (-x);
}
// 快速log2 很巧妙的思路
int log2_fast(int n) {
int result = 0;
if(n&0xffff0000) { result += 16; n >>= 16; }
if(n&0x0000ff00) { result += 8; n >>= 8; }
if(n&0x000000f0) { result += 4; n >>= 4; }
if(n&0x0000000c) { result += 2; n >>= 2; }
if(n&0x00000002) { result += 1; n >>= 1; }
return result;
}
double gougu(point x, point y) {
double xc, yc;
xc = abs(x.x - y.x);
yc = abs(x.y - y.y);
return sqrt(xc*xc+yc*yc);
}
// 用引用节省内存,假设递归次数较多这个是重要的!!!
void dfs(double di, int state, point& be) {
// 剪枝优化, 根据已走的路程判断当前分支是否可以做得更好
if(di >= ans) {
return;
}
// 深搜结束
if(state == all) {
// 选择最优
ans = min(di, ans);
return;
}
// 解析状压,转换为二进制其实使用 int vis = all - state; 效果也是一摸一样的
int vis = all&(~state);
for(int i=vis; i; i -= lowbit(i)) {
int x = lowbit(i);
// 转换为数组下标
int j = log2_fast(x);
// 求出给下一个深搜的参数
int nextState = state+x;
double nextdi = di+gougu(be, a[j]);
// 剪枝,很简单的道理,这条路走了,就不走了
// 记录成double的原因是就算当前坐在的节点和状态相同,走的距离也可以不相同,所以需要更多的信息作比较
if(dp[nextState][j]!=0 && dp[nextState][j]<=nextdi) {
continue;
}
// 记录
dp[nextState][j] = nextdi;
// 接着深搜
dfs(nextdi, nextState, a[j]);
}
}
int main() {
// 初始化
cin >> n;
all = (1<<n)-1;
for(int i = 0; i<n; i++) {
cin >> a[i].x >> a[i].y;
}
point t;
t.x = t.y = 0;
// 深搜的参数有三个,分别为距离,状压后的是否使用和当前点
dfs(0, 0, t);
printf("%.2f", ans);
return 0;
}
/**
* 本程序使用了map来,记忆化勾股定理的数,map很慢,会超时,实测不使用map来存储也可以过
* 非要用map的话就吸氧罢(O2),吸氧后的和不吸氧的普通的差不多耗时
**/
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
#include <map>
using namespace std;
int all, n;
double ans = 200000000;
struct point {
double x, y;
} a[100];
map<pair<double, double>, double> dis;
double dp[1 << 15][20] = {};
int lowbit(int x) {
return x & (-x);
}
// 快速log2 很巧妙的思路
int log2_fast(int n) {
int result = 0;
if(n&0xffff0000) { result += 16; n >>= 16; }
if(n&0x0000ff00) { result += 8; n >>= 8; }
if(n&0x000000f0) { result += 4; n >>= 4; }
if(n&0x0000000c) { result += 2; n >>= 2; }
if(n&0x00000002) { result += 1; n >>= 1; }
return result;
}
double gougu(point x, point y) {
// 算差,利用差来记忆数据
double xc, yc;
xc = abs(x.x - y.x);
yc = abs(x.y - y.y);
if(xc > yc) {
swap(xc, yc);
}
pair<double, double> t(xc, yc);
if(dis[t] != 0) {
return dis[t];
}
return dis[t] = sqrt(xc*xc+yc*yc);
}
// 用引用节省内存,假设递归次数较多这个是重要的!!!
void dfs(double di, int state, point& be) {
// 剪枝优化, 根据已走的路程判断当前分支是否可以做得更好
if(di >= ans) {
return;
}
// 深搜结束
if(state == all) {
// 选择最优
ans = min(di, ans);
return;
}
// 解析状压,转换为二进制其实使用 int vis = all - state; 效果也是一摸一样的
int vis = all&(~state);
for(int i=vis; i; i -= lowbit(i)) {
int x = lowbit(i);
// 转换为数组下标
int j = log2_fast(x);
// 求出给下一个深搜的参数
int nextState = state+x;
double nextdi = di+gougu(be, a[j]);
// 剪枝,很简单的道理,这条路走了,就不走了
// 记录成double的原因是就算当前坐在的节点和状态相同,走的距离也可以不相同,所以需要更多的信息作比较
if(dp[nextState][j]!=0 && dp[nextState][j]<=nextdi) {
continue;
}
// 记录
dp[nextState][j] = nextdi;
// 接着深搜
dfs(nextdi, nextState, a[j]);
}
}
int main() {
// 初始化
dis.clear();
cin >> n;
all = (1<<n)-1;
for(int i = 0; i<n; i++) {
cin >> a[i].x >> a[i].y;
}
point t;
t.x = t.y = 0;
// 深搜的参数有三个,分别为距离,状压后的是否使用和当前点
dfs(0, 0, t);
printf("%.2f", ans);
return 0;
}
写这篇题解的时候运气太背啦!!!
先是luogu爆网关,然后博客园爆网关
感觉最近互联网不太平啊
本文来自博客园,作者:月神的使者,转载请注明原文链接:https://www.cnblogs.com/dffxd/p/17204836.html