Mobile Service \operatorname{Mobile\ Service} Mobile Service
题目链接: j z o j 1327 jzoj\ 1327 jzoj 1327 / l u o g u S P 703 luogu\ SP703 luogu SP703
题目
一个公司有三个移动服务员。如果某个地方有一个请求,某个员工必须赶到那个地方去(那个地方没有其他员工),某一时刻只有一个员工能移动。被请求后,他才能移动,不允许在同样的位置出现两个员工。从 p p p 到 q q q 移动一个员工,需要花费 c ( p , q ) c(p,q) c(p,q) 。这个函数没有必要对称,但是 c ( p , p ) = 0 c(p,p)=0 c(p,p)=0 。公司必须满足所有的请求。目标是最小化公司花费。
输入
第一行有两个整数 L , N ( 3 < = L < = 200 , 1 < = N < = 1000 ) L,N(3\!<=\!L\!<=\!200, 1\!<=\!N\!<=\!1000) L,N(3<=L<=200,1<=N<=1000) 。 L L L 是位置数; N N N 是请求数。每个位置从 1 1 1 到 L L L 编号。下L行每行包含 L L L 个非负整数。第 i + 1 i+1 i+1 行的第 j j j 个数表示 c ( i , j ) c(i,j) c(i,j) ,并且它小于 2000 2000 2000 。最后一行包含 N N N 个数,是请求列表。一开始三个服务员分别在位置 1 1 1 , 2 2 2 , 3 3 3 。
输出
一个数 M M M,表示最小服务花费。
样例输入
5 9
0 1 1 1 1
1 0 2 3 2
1 1 0 4 1
2 1 5 0 1
4 2 3 4 0
4 2 4 1 5 4 3 2 1
样例输出
5
思路
一道dp。
设
f
[
i
]
[
j
]
[
k
]
[
l
]
f[i][j][k][l]
f[i][j][k][l] 表示第
i
i
i 次请求中, 原来在位置
j
j
j ,三个服务员分别在
j
j
j ,
k
k
k ,
l
l
l 这三个位置。
那状态转移方程就是这些:
f[i+1][a[i+1]][k][l]=min(f[i+1][a[i+1]][k][l],f[i][j][k][l]+c[j][a[i+1]];
f[i+1][j][a[i+1]][l]=min(f[i+1][j][a[i+1]][l],f[i][j][k][l]+c[k][a[i+1]];
f[i+1][j][k][a[i+1]]=min(f[i+1][j][k][a[i+1]],f[i][j][k][l]+c[s][a[i+1]];
(a[i]指第i次移动到哪里)
然后我们就会发现每一个
f
f
f 里面都有
a
[
i
+
1
]
a[i+1]
a[i+1] ,又因为会TLE+MLE,我们可以把
a
[
i
+
1
]
a[i+1]
a[i+1] 去掉。
(
不
要
问
我
为
什
么
可
以
去
掉
,
我
也
不
太
清
楚
)
{\color{white}(不要问我为什么可以去掉,我也不太清楚)}
(不要问我为什么可以去掉,我也不太清楚)
那就变成了这个:
f[i+1][j][k]=min(f[i+1][j][k],f[i][j][k]+c[a[i]][a[i+1]];
f[i+1][a[i]+1][k]=min(f[i+1][a[i]+1][k],f[i][j][k]+c[j][a[i+1]];
f[i+1][j][a[i+1]]=min(f[i+1][j][a[i+1]],f[i][j][k]+c[k][a[i+1]];
然后就可以了。
代码
#include<cstdio>
#include<cstring>
#include<iostream>
using namespace std;
int l, n, c[201][201], a[1001], f[2][201][201], ans, x;
int main() {
scanf("%d %d", &l, &n);//读入
for (int i = 1; i <= l; i++)
for (int j = 1; j <= l; j++)
scanf("%d", &c[i][j]);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
memset(f, 0x7f, sizeof(f));//初始化
f[0][1][2] = 0;
a[0] = 3;
for (int i = 0; i < n; i++) {
x ^= 1;//滚动数组
memset(f[x], 0x7f, sizeof(f[x]));
for (int j = 1; j <= l; j++)
for (int k = 1; k <= l; k++)
if (j != k && a[i] != j && a[i] != k) {
f[x][j][k] = min(f[x][j][k], f[x ^ 1][j][k] + c[a[i]][a[i + 1]]);//从谁过来
f[x][a[i]][k] = min(f[x][a[i]][k], f[x ^ 1][j][k] + c[j][a[i + 1]]);
f[x][j][a[i]] = min(f[x][j][a[i]], f[x ^ 1][j][k] + c[k][a[i + 1]]);
}
}
ans = 2147483647;
for (int i = 1; i <= l; i++)
for (int j = 1; j <= l; j++)
ans = min(ans, f[x][i][j]);//找到最便宜的答案
printf("%d", ans);
return 0;
}