题意
一个公司有三个移动服务员,最初分别在位置 1,2,3处。
如果某个位置(用一个整数表示)有一个请求,那么公司必须指派某名员工赶到那个地方去。
某一时刻只有一个员工能移动,且不允许在同样的位置出现两个员工。
从 p 到 q 移动一个员工,需要花费 c(p,q)。
这个函数不一定对称,但保证 c(p,p)=0。
给出 N 个请求,请求发生的位置分别为 p1∼pN。
公司必须按顺序依次满足所有请求,且过程中不能去其他额外的位置,目标是最小化公司花费,请你帮忙计算这个最小花费。
思路
线性dp:
状态表示: f [ i ] [ x ] [ y ] f[i][x][y] f[i][x][y] 表示已经处理完前 i 个请求 并且服务员在 p[i]、x、y 位置上的所有状态的集合
**属性:**最小值
**状态计算:**由状态 i i i 向 i + 1 i + 1 i+1转移比较方便 有三种情况
-
位于 p [ i ] p[i] p[i] 的服务员出发前往 p [ i + 1 ] p[i + 1] p[i+1] ,此时状态变成
f [ i + 1 ] [ x ] [ y ] = m i n ( f [ i + 1 ] [ x ] [ y ] , f [ i ] [ x ] [ y ] + w [ p [ i ] ] [ p [ i + 1 ] ] ) f[i + 1][x][y] = min(f[i + 1][x][y],f[i][x][y] + w[p[i]][p[i + 1]]) f[i+1][x][y]=min(f[i+1][x][y],f[i][x][y]+w[p[i]][p[i+1]])
-
位于 x x x 的服务员出发前往 p [ i + 1 ] p[i + 1] p[i+1] ,此时状态变成
f [ i + 1 ] [ p [ i ] ] [ y ] = m i n ( f [ i + 1 ] [ p [ i ] ] [ y ] , f [ i ] [ x ] [ y ] + w [ x ] [ p [ i + 1 ] ] ) f[i + 1][p[i]][y] = min(f[i + 1][p[i]][y],f[i][x][y] + w[x][p[i + 1]]) f[i+1][p[i]][y]=min(f[i+1][p[i]][y],f[i][x][y]+w[x][p[i+1]])
-
位于 y y y 的服务员出发前往 p [ i + 1 ] p[i + 1] p[i+1] ,此时状态变成
f [ i + 1 ] [ x ] [ p [ i ] ] = m i n ( f [ i + 1 ] [ x ] [ p [ i ] ] , f [ i ] [ x ] [ y ] + w [ y ] [ p [ i + 1 ] ] ) f[i + 1][x][p[i]] = min(f[i + 1][x][p[i]],f[i][x][y] + w[y][p[i + 1]]) f[i+1][x][p[i]]=min(f[i+1][x][p[i]],f[i][x][y]+w[y][p[i+1]])
r e s = m i n ( f [ m ] [ i ] [ j ] ) i , j ∈ [ 1 , n ] res = min(f[m][i][j]) \quad \quad i,j \in [1,n] res=min(f[m][i][j])i,j∈[1,n]
这题首先肯定想到 用 f [ i ] [ x ] [ y ] [ z ] f[i][x][y][z] f[i][x][y][z] 表示状态 但是显然复杂度超了 所以考虑优化掉一维
由于不管怎么走 计算到 i + 1 i+1 i+1 时 肯定有一个服务员在 p [ i ] p[i] p[i] 所以只要枚举两个服务员的位置即可
不允许两个员工出现在同一个位置 所以要特判一下
代码
#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define mod 998244353
#define endl '\n'
//#define int long long
using namespace std;
inline int gcd(int a, int b) { return b ? gcd(b, a%b) : a; }
inline int lowbit(int x) { return x & -x; }
typedef long long LL;
typedef pair<int, int>PII;
const int N = 210, M = 1010;
int n, m;
int w[N][N];
int f[M][N][N];
int p[M];
void solve() {
cin >> n >> m;
for (int i = 1; i <= n; ++i)
for (int j = 1; j <= n; ++j)
scanf("%lld", &w[i][j]);
for (int i = 1; i <= m; ++i)
cin >> p[i];
memset(f, 0x3f, sizeof f);
p[0] = 3;
f[0][1][2] = 0;
for (int i = 0; i < m; ++i) {
for (int x = 1; x <= n; ++x) {
for (int y = 1; y <= n; ++y) {
if (x == y || x == p[i] || y == p[i])continue;
f[i + 1][x][y] = min(f[i + 1][x][y], f[i][x][y] + w[p[i]][p[i + 1]]);
f[i + 1][p[i]][y] = min(f[i + 1][p[i]][y], f[i][x][y] + w[x][p[i + 1]]);
f[i + 1][x][p[i]] = min(f[i + 1][x][p[i]], f[i][x][y] + w[y][p[i + 1]]);
}
}
}
int res = INF;
for (int x = 1; x <= n; ++x) {
for (int y = 1; y <= n; ++y) {
if (x == y || x == p[m] || y == p[m])continue;
res = min(res, f[m][x][y]);
}
}
cout << res << endl;
}
signed main() {
//int t; cin >> t;
//while(t--)
solve();
return 0;
}