service
【问题描述】
一家公司为它在各地的用户提供服务,公司有三名负责这项工作的员工,分别编号为1,2,3,服务的地点有n个,分别编号为1,2,3,...n,把从编号为p的服务地点直接到达编号为q的服务地点所需的移动费用记为C(p,q),显然C(p,p)=0(停留在原地不需要费用),但不保证对任意p,q均有C(p,q)=C(q,p)。
初始时员工1在地点1,员工2在地点2,员工3在地点3,现在公司依次收到了L个服务请求,每个请求需要一名员工赶到其指定的地点进行服务,员工可以选择直达,也可以选择经过若干个服务地点中转,特别地,如果指派的员工已在当前请求所在地,则该请求不需要任何移动费用即可被处理。
出于公平起见,所有请求必须按顺序处理,这意味着即使一名员工在赶往当前请求的途中经过之后的请求所在的地点,他也不可以先处理之后的请求,但是公司不限制每位员工赶往请求地点的路线,也允许一个服务地点有多名员工。
你的任务就是对于这L个请求,找到一个服务方案(即对每个请求分配合适的员工去服务以及规划移动路线),使得三名员工提供服务的总移动费用最小。
【输入格式】
每个测试点第一行为一个正整数T,表示该测试点内的数据组数,你需要对该测试点内的T组数据都分别给出正确的答案才能获得该测试点的分数。
接下来T组数据,每组数据第一行两个正整数n和L,含义如题目所述,接下来n行,每行n个非负整数,其中第p行第q个数表示C(p,q),最后一行包含一行L个正整数,依次描述每个请求指定的地点。
【输出格式】
对每组数据输出一行一个非负整数,表示最小的总移动费用。
【输入输出样例】
service.in |
service.out |
2 3 6 0 1 2 3 0 4 5 6 0 1 2 3 1 2 3 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 |
0 5 |
【样例解释】
第一组测试数据中,所有可能的地点都有员工,因此不需要进行任何移动。
第二组测试数据中,三位员工初始在1 2 3三个地方,下面给出其中一种达到最小总移动费用的方案:
请求地点 |
指派员工 |
移动路线 |
员工所在地 |
当前总代价 |
(初始状态) |
1 2 3 |
0 |
||
4 |
1 |
1->4 |
4 2 3 |
0+1=1 |
2 |
2 |
2 |
4 2 3 |
1+0=0 |
4 |
1 |
4 |
4 2 3 |
1+0=1 |
1 |
2 |
2->1 |
4 1 3 |
1+1=2 |
5 |
2 |
1->5 |
4 5 3 |
2+1=3 |
4 |
1 |
4 |
4 5 3 |
3+0=3 |
3 |
3 |
3 |
4 5 3 |
3+0=3 |
2 |
1 |
4->2 |
2 5 3 |
3+1=4 |
1 |
3 |
3->1 |
2 5 1 |
4+1=5 |
【数据规模与约定】
每个测试点5分,各个测试点数据范围如下:
测试点编号 |
n |
L |
1-3 |
n<=10 |
L<=10 |
4-6 |
n<=40 |
L<=10 |
7-10 |
n<=40 |
L<=100 |
11-14 |
n<=100 |
L<=100 |
15-17 |
n<=100 |
L<=500 |
18-20 |
n<=200 |
L<=500 |
对于所有的测试点,均有数据组数T<=5,地点数n>=3,给定的C矩阵主对角线上的数全部为0,且输入数据中所有的数均为不超过2000的非负整数。
同TYVJ1061【mobile service】
乍一眼看过去,不就是个dp吗,啪啪啪四维dp码完,看复杂度感觉有点不对啊,数组好像有点大啊,面临TLE和MLE的双风险。
很明显,这个四维dp的解法十分不优秀。
f[i][x][y][z]表示到了第i个阶段,x,y,z分别表示三个人的位置。
然后转移考虑最小代价即可。
思考优化:去除冗杂信息。
对于这三个人的位置,只需要得知两个,要么更新的是这两个其中之一,两个人位置都不更新就是剩下的一个位置进行了更新。
只需三维,代码如下:
ps:为什么要用floyd求任意两点之间的最短距离。(本题与tyvj1061的区别)
是否限制每个地点的员工数量。这点没看出来也是这题的丢分原因,我没有floyd。
给出service的代码
#include<bits/stdc++.h>
using namespace std;
const int maxn = +;
const int maxL = +;
const int inf = 1e9;
int T,n,L,ans;
int c[maxn][maxn],f[maxL][maxn][maxn],p[maxL];
inline int read(){
int x=,f=; char ch=getchar();
while(ch<''||ch>''){if(ch=='-')f=-;ch=getchar();}
while(ch>=''&&ch<=''){x=x*+ch-'';ch=getchar();}
return x*f;
}
void work(){
p[] = ;
f[][][] = ;//记录1,2的状态
for(int i = ;i <= L; ++i){
for(int j = ;j <= n; ++j){
for(int k = ;k <= n; ++k){
f[i][j][k] = min(f[i][j][k],f[i-][j][k]+c[p[i-]][p[i]]);
f[i][p[i-]][k] = min(f[i][p[i-]][k],f[i-][j][k]+c[j][p[i]]);
f[i][j][p[i-]] = min(f[i][j][p[i-]],f[i-][j][k]+c[k][p[i]]);
}
}
}
for(int i = ;i <= n; ++i){
for(int j = ;j <= n; ++j){
ans = min(ans,f[L][i][j]);
}
}
printf("%d\n",ans);
}
int main(){
freopen("service.in","r",stdin);
freopen("service.out","w",stdout);
T = read();
while(T--){
ans = inf;
memset(f,0x3f,sizeof(f));
n =read(); L = read();
for(int i = ;i <= n; ++i){
for(int j = ;j <= n; ++j){
c[i][j] = read();
}
}
for(int i =;i <= L; ++i) p[i] = read();
for(int k = ;k <= n; ++k){
for(int i = ;i <= n; ++i){
for(int j = ;j <= n; ++j){
c[i][j] = min(c[i][j],c[i][k]+c[k][j]);
}
}
}
work();
}
return ;
}