先来看,最终的目标是什么完成N个请求,且在第i个请求完成时,与前面的任务没有什么影响。
完成N个任务,跟谁有关,可以看到每一次有三名服务员,都可以派,具体派谁看谁去完成这个任务花费最少,花费与服务员上一轮所在的位置有关,则服务员的位置需要记录一下。
入此我们就能算出第i个任务结束,要完成第i+1个任务,派每个服务员所需产生的花费,都求出来找一个min即可。
Y以每一个任务为阶段
定义状态:f(i,x,y,z)完成前i个请求,服务员在x、y、z处所进行的花费。
考虑 F[i,x,y,z]能够更新i+1阶段的哪些状态。转移显然有三种,就是派三员工之一去第i+1个请求处。
-
派x服务员工作,则
f(i+1,pi+1,y,z)=min( f(i+1,pi+1,y,z) , f(i,x,y,z)+c(x,pi+1) ) -
派y服务员工作,则
f(i+1,x,pi+1,z)=min( f(i+1,x,pi+1,z) , f(i,x,y,z)+c(y,pi+1) ) -
派z服务员工作,则
f(i+1,x,y,pi+1)=min( f(i+1,x,y,pi+1) , f(i,x,y,z)+c(z,pi+1) )
注意:要判断两个服务员不能出现在同一个地方的情况
时间复杂度 1000200200*200
优化
优化的方式可以考虑, 某一维是否可以用其他维度表示出来,如果可以,则这一维可以去掉
阶段不能去,三个服务员的位置,我们知道当第i个任务完成后,三个服务员中肯定有一个位置位于 pi 处。
就可以将完成第i个任务的人的位置为 pi,另外两个服务员的位置为x,y。
即 f(i,x,y) 为完成前i个请求,一个服务员位于 pi,另外两个分别位于x、y。
- 派位置位于 pi 的人去干活
f( i+1, x,y)=min( f(i+1,x,y) , f(i,x,y)+c(pi,pi+1) - 派位置位于 x的人去干活
f( i+1, pi,y)=min( f(i+1,pi,y) , f(i,x,y)+c(x,pi+1) - 派位置位于y的人去干活
f( i+1, x,pi)=min( f(i+1,x,pi) , f(i,x,y)+c(y,pi+1)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
启示:
(1)先确定阶段,如果阶段不足以表示一个状态,则一些附加信息也可以作为附加维度。
(2)优化维度,可以判断某一个维度是否可以用其他维度表示出来,如完成上一轮任务的服务员的位置就在pi,减少枚举一个服务员的位置量。
const int N = 1010, M = 210;
int n, m, f[N][M][M], c[M][M], q[N];
int main()
{
cin >> m >> n;
for (int i = 1; i <= m; i++)
for (int j = 1; j <= m; j++)
cin >> c[i][j];
for (int i = 1; i <= n; i++) cin >> q[i];
memset(f, 0x3f, sizeof f);
q[0] = 3;
f[0][1][2] = 0;
for (int i = 0; i < n; i++)
for (int a = 1; a <= m; a++)
for (int b = 1; b <= m; b++)
{
//同一个位置不可
if (q[i] == a || q[i] == b || a == b) continue;
int v = f[i][a][b];//上一个任务的值
int x = q[i], y = q[i + 1];
//三种方案
f[i + 1][a][b] = min(f[i + 1][a][b], v + c[x][y]);
f[i + 1][x][b] = min(f[i + 1][x][b], v + c[a][y]);
f[i + 1][a][x] = min(f[i + 1][a][x], v + c[b][y]);
}
int res = 0x3f3f3f3f;
for (int i = 1; i <= m; i++)
for (int j = 1; j <= m; j++)
{
if (i == j || i == q[n] || j == q[n]) continue;
res = min(res, f[n][i][j]);
}
cout << res << endl;
}