传送门
真是一道好题,最近写了不少DP,对状态的设计也大概有了个掌握,知道什么情况下可以设计什么样的状态。这道题虽然也看了一下蓝书上的题解,但跟自己独立想的还是差不多的。也是很难得没有看代码AC出来。。
我们可以设F[i][x][y][z]表示完成了i个请求,三个员工分别位于x,y,z的位置时,公司的花费。但可以推出的是,完成i请求后,必定有一个员工的位置在request[i],因此这个位置就是一个多余的信息。可以将状态缩小一个维度,在DP中要尽量用少的维度覆盖问题。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
int l, n;
int cost[205][205];
int request[1005];
int dp[1005][205][205]; //dp[i][j][k]表示完成到i任务,三人目前位置是request[i], j, k
inline bool judge(int a, int b, int c){
if(a != b && a != c && b != c) return true;
return false;
}
int main(){
ios::sync_with_stdio(0);
cin.tie(0);
int t;
cin >> t;
while(t--){
memset(dp, 0x3f3f3f3f, sizeof(dp));
cin >> l >> n;
for(int i = 1; i <= l; ++i)
for(int j = 1; j <= l; ++j)
cin >> cost[i][j];
for(int i = 1; i <= n; ++i)
cin >> request[i];
request[0] = 1;
dp[0][2][3] = dp[0][3][2] = 0;
for(int i = 0; i < n; ++i){
for(int j = 1; j <= l; ++j){
for(int k = 1; k <= l; ++k){
if(judge(request[i], j, k)){
if(judge(request[i+1], j, k))
dp[i+1][j][k] = dp[i+1][k][j] = min(dp[i+1][j][k], dp[i][j][k]+cost[request[i]][request[i+1]]);
if(judge(request[i+1], request[i], k))
dp[i+1][request[i]][k] = dp[i+1][k][request[i]] = min(dp[i+1][k][request[i]], dp[i][j][k]+cost[j][request[i+1]]);
if(judge(request[i+1], request[i], j))
dp[i+1][request[i]][j] = dp[i+1][j][request[i]] = min(dp[i+1][j][request[i]], dp[i][j][k]+cost[k][request[i+1]]);
}
}
}
}
int ans = 0x3f3f3f3f;
for(int i = 1; i <= l; ++i)
for(int j = 1; j <= l; ++j)
if(ans > dp[n][i][j]) ans = dp[n][i][j];
cout << ans << endl;
}
return 0;
}