AcWing 274. 移動服務
題目
在平面上有 L 個點,有三個服務員在初始給出的三個點上,並且題目給出平面上點兩兩之間的距離。隨後有 n 個請求,每發出一個請求,需要派一個服務員去(只能去一個),花費就是距離。問滿足所有請求的最小花費。L < 200, n < 1000
分析
首先爆搜肯定能出結果,對於每個請求枚舉三個服務員。
思考如何用動態規劃做:
直觀表示(將所有維度保存下來): 代表已經處理 i 個請求,且三個服務員在 x,y,z 點。但是如果能找出維度之間的關係就能優化,比如處理第 i 個請求肯定要派人去,那麼三人之中一個人的位置就是 ,因此狀態可以優化爲:
①: 狀態表示(經驗)
- 集合: 表示所有已經處理完前 i 個請求,且另外兩個服務員在 x,y 兩地的所有安排方案
- 屬性:表示集合中所有方案路徑花費之和的最小值。
②: 狀態轉移
對於一般 dp,都是用 所依賴的狀態更新它自己。還有一種就是用當前的更新別人。 回到這個題,入邊的情況很複雜,而每個狀態一定會導出三種狀態。(就是填表和刷表)
對於每一個請求,有三個服務員可以派去,枚舉 x,y,z
複雜度
#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);
}