洛谷 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;
}