AcWing 274. 移動服務 (遞推三種出邊dp)

AcWing 274. 移動服務

題目

在平面上有 L 個點,有三個服務員在初始給出的三個點上,並且題目給出平面上點兩兩之間的距離。隨後有 n 個請求,每發出一個請求,需要派一個服務員去(只能去一個),花費就是距離。問滿足所有請求的最小花費。L < 200, n < 1000

分析

首先爆搜肯定能出結果,對於每個請求枚舉三個服務員。

思考如何用動態規劃做:

直觀表示(將所有維度保存下來):dp[i][x][y][z]dp[i][x][y][z] 代表已經處理 i 個請求,且三個服務員在 x,y,z 點。但是如果能找出維度之間的關係就能優化,比如處理第 i 個請求肯定要派人去,那麼三人之中一個人的位置就是 p[i]p[i],因此狀態可以優化爲:

①: 狀態表示(經驗)

  1. 集合:dp[i][x][y]dp[i][x][y] 表示所有已經處理完前 i 個請求,且另外兩個服務員在 x,y 兩地的所有安排方案
  2. 屬性:表示集合中所有方案路徑花費之和的最小值

②: 狀態轉移

對於一般 dp,都是用 dp[i][j]dp[i][j]所依賴的狀態更新它自己。還有一種就是用當前的更新別人。 回到這個題,入邊的情況很複雜,而每個狀態一定會導出三種狀態。(就是填表和刷表)

對於每一個請求,有三個服務員可以派去,枚舉 x,y,z
複雜度 O(n2L)O(n2 L)

#include <bits/stdc++.h>
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);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章