洛谷 P1433 吃奶酪

状态压缩,和剪枝

最优化剪枝:如果距离大于当前最短距离,则必然不最优 。

      如果当前连通块到某点的距离大于之前记录的距离,则该走法不最优。(dp[i][j]实现)

状态压缩:用二进制的每一位表示某个点是否访问过,例如第一个点访问过则二进制第一位为1.

 

首先我们在状态中有一个 当前到了数组哪个点的状态,命名为nowdot。每次定义p为当前点到第i个的距离,用二进制保存,初始化为int p = nowdot + (1 << (i - 1)),如果从p到i的距离已经有过更新并且本次构造的距离还大于之前,则跳过;否则更新,继续搜索。

 

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int N = 20;
int n;
double ans = 1e9, dp[66000][20]; //dp[i][j]i表示压缩的状态表示走过的点,j表示当前状态最后到达的一个点 
struct node {
	double x;
	double y;
}a[N];
double discal (int l, int m) { //距离计算函数 
	return sqrt((a[l].x - a[m].x) * (a[l].x - a[m].x) 
			+ (a[l].y - a[m].y) * (a[l].y - a[m].y)); 
}
bool vis[N];//点标记,防止重复访问 
//dfs(已经吃了多少个,当前吃的第几个,当前跑了多远,当前走过的点状态)
void dfs(int sum, int nowi, double dis, int nowdot) {
	//最优化剪枝,如果距离大于当前最短距离,则必然不最优 
	if (dis > ans) {
		return;
	} 
	//搜索正常结束条件 
	if (sum == n) {
		if (dis < ans) ans = dis;
		return;
	}
	for (int i = 1; i <= n; i++) {
		if (!vis[i]) {
			int p = nowdot + (1 << (i - 1)); 
			//二进制状态压缩从当前点到这个点 
			if (dp[p][i] != 0 && dp[p][i] <= dis + discal(nowi, i)) 
				continue;
			
			vis[i] = 1;//标记防止重复访问 
			dp[p][i] = dis + discal(nowi, i);
			dfs(sum + 1, i, dp[p][i], p);
			vis[i] = 0;//取消标记 
		}
	}
}
int main() {
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> a[i].x >> a[i].y;
	}
	dfs(0, 0, 0, 0);
	printf("%.2lf", ans);
	return 0;
}

 

posted @ 2023-07-24 17:13  浪矢-CL  阅读(13)  评论(0编辑  收藏  举报