AcWing 274. 移动服务 (递推三种出边dp)

AcWing 274. 移动服务

题目

在平面上有 L 个点,有三个服务员在初始给出的三个点上,并且题目给出平面上点两两之间的距离。随后有 n 个请求,每发出一个请求,需要派一个服务员去(只能去一个),花费就是距离。问满足所有请求的最小花费。L < 200, n < 1000

分析

首先爆搜肯定能出结果,对于每个请求枚举三个服务员。

思考如何用动态规划做:

直观表示(将所有维度保存下来): d p [ i ] [ x ] [ y ] [ z ] dp[i][x][y][z] dp[i][x][y][z] 代表已经处理 i 个请求,且三个服务员在 x,y,z 点。但是如果能找出维度之间的关系就能优化,比如处理第 i 个请求肯定要派人去,那么三人之中一个人的位置就是 p [ i ] p[i] p[i],因此状态可以优化为:

①: 状态表示(经验)

  1. 集合: d p [ i ] [ x ] [ y ] dp[i][x][y] dp[i][x][y] 表示所有已经处理完前 i 个请求,且另外两个服务员在 x,y 两地的所有安排方案
  2. 属性:表示集合中所有方案路径花费之和的最小值

②: 状态转移

对于一般 dp,都是用 d p [ i ] [ j ] dp[i][j] dp[i][j]所依赖的状态更新它自己。还有一种就是用当前的更新别人。 回到这个题,入边的情况很复杂,而每个状态一定会导出三种状态。(就是填表和刷表)

对于每一个请求,有三个服务员可以派去,枚举 x,y,z
复杂度 O ( n 2 L ) O(n2 L) O(n2L)

#include 
using namespace std;
typedef long long ll;
const ll INF = 0x3f3f3f3f;
const int N = 1e3 + 5;
const int M = 205;

int l, n;
int w[M][M];
int p[N], dp[N][M][M];

int main() {
	scanf("%d%d", &l, &n);
	for (int i = 1; i <= l; i++) 
		for (int j = 1; j <= l; j++) 
			scanf("%d", &w[i][j]);
	for (int i = 1; i <= n; i++) 
		scanf("%d", &p[i]);
	memset(dp, INF, sizeof(dp));
	p[0] = 3;	// 假设第 0 个请求在 3 号点
	dp[0][1][2] = 0;
	for (int i = 0; i < n; i++) 
		for (int x = 1; x <= l; x++) {
			for (int y = 1; y <= l; y++) {
				int z = p[i], u = p[i+1];
				int v = dp[i][x][y];
				if (x == y || y == z || x == z) continue;
				dp[i + 1][x][y] = min(dp[i + 1][x][y], v + w[z][u]);	// 派 z 去
				dp[i + 1][z][y] = min(dp[i + 1][z][y], v + w[x][u]);	// 派 x 去
				dp[i + 1][x][z] = min(dp[i + 1][x][z], v + w[y][u]);	// 派 z 去
			}
		}
	int ans = INF;
	for (int x = 1; x <= l; x++) {
		for (int y = 1; y <= l; y++) {
			int z = p[n];
			if (x == y || y == z || x == z) continue;
			ans = min(ans, dp[n][x][y]);
		}
	}
	printf("%d\n", ans);
}

你可能感兴趣的:(解题报告,线性dp,其他oj)