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爆网关,然后博客园爆网关
感觉最近互联网不太平啊
image
image

posted @ 2023-03-10 22:23  月神的使者  阅读(36)  评论(0编辑  收藏  举报