洛谷P1433 吃奶酪(状压DP)

传送门 难度
https://www.luogu.com.cn/problem/P1433 普及+/提高

分析符号说明

  • i:状态压缩的路径
  • j:到达的点
  • k:到达j之前到达的那一个点
  • n:一共n个点
  • point(x):到达x这一个点的二进制表示
  • s[][]:s[i][j]表示从i到j的距离
  • f[][]:动态规划数组,f[i][j]表示经过路径i到达j所需的最小路径

分析

  1. 这道题目用DFS的话最后一个测试点会出现超时的情况。所以换用状压DP的方式求解。
  2. i是二进制表示路径的数。例如: i = ( 1010 ) 2 i=(1010)_2 i=(1010)2 ,就可以表示到了第2、第4两个点,没到第1、第3两个点。
  3. 状态转移方程
    3.1. f [ i , j ] = m i n { f [ i − p o i n t ( j ) ] + s [ j ] [ k ] ∣ 0 ≤ k < n } f[i,j]=min\{f[i- point(j)]+s[j][k]|0\leq kf[i,j]=min{f[ipoint(j)]+s[j][k]0k<n}
    3.2. 注意点1:i这条路径要包含j
    3.3. 注意点2: i去掉j的这条路径要包含k

AC代码

#include
#include
#include
#include
#include

using namespace std;

const double INF = 23333333333.0;

const int N = 17, M = 1 << N;
double s[N][N];//记录距离
double mp[N][5];//读入输入
double f[M][N];//dp数组
int n;

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


int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; ++i) {
		cin >> mp[i][0] >> mp[i][1];

	}
	for (int i = 0; i < n; ++i)
		for (int j = i + 1; j <= n; ++j) {
			double tem = dis(mp[i][0], mp[i][1], mp[j][0], mp[j][1]);
			s[i][j] = s[j][i] = tem;
		}
	n++;//算上原点
	for (int i = 0; i <= 1 << n; ++i) {
		for (int j = 0; j < n; ++j)
			f[i][j] = INF;
	}
	f[1][0] = 0.0;//原点初始化

	for (int i = 0; i < (1 << n); ++i)//枚举状态
		for (int j = 0; j < n; ++j)
			if ((i >> j) & 1)
				for (int k = 0; k < n; ++k)//上一个点为k
					if ((i - (1 << j)) >> k & 1)
						f[i][j] = min(f[i][j], f[i - (1 << j)][k] + s[k][j]);

	double res = INF;
	int allin = (1 << n) - 1;
	for (int i = 1; i < n; ++i)
		res = min(res, f[allin][i]);
	printf("%.2f", res);

	return 0;
}

你可能感兴趣的:(DP—状压DP)