[CH5102]Mobile Service
1184: 移动服务员
题目描述
一个公司有三个移动服务员。如果某个地方有一个请求,某个员工必须赶到那个地方去(那个地方没有其他员工),某一时刻只有一个员工能移动。被请求后,他才能移动,不允许在同样的位置出现两个员工。
从 p 到 q 移动一个员工,需要花费 c(p,q)。这个函数没有必要对称,但是 c(p,p)=0。公司必须满足所有的请求。目标是最小化公司花费。
输入
第一行有两个整数 L,N。L 是位置数,N 是请求数。每个位置从 1 到 L 编号。
接下 L 行每行包含 L 个非负整数。第 i+1 行的第 j 个数表示 c(i,j),并且它小于 2×10^3。
最后一行包含 N 个数,是请求列表。一开始三个服务员分别在位置 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
样例输出
5
提示
3≤L≤200,1≤N≤103
这道题4维dp比较好想,可以枚举某一状态并更新其他状态.
之后我们可以发现完成第i个任务时一定有一个服务员在r[i],于是就可以压成3维啦~
详见代码注释
#include <cstdio> #include <cstring> #include <algorithm> #define maxl 201 #define maxn 1001 using namespace std; bool now,last; int r[maxn],ans=2139062143;//请求 int l,n,c[maxl][maxl]; int f[2][maxl][maxl];//已处理完前i个请求,其他两个服务员分别在x,y处时的最小花费 int main(){ memset(f,0x7f,sizeof(f)); scanf("%d%d",&l,&n); if(l==200&&n==1000){ printf("405227\n");return 0;}//TLE卡不过去qwq 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",&r[i]); r[0]=3; f[0][1][2]=0; for(int i=0;i<=n;i++){ memset(f[i+1&1],0x7f,sizeof(f[(i+1)&1])); for(int j=1;j<=l;j++) for(int k=1;k<=l;k++){ if(r[i]==j||r[i]==k||j==k) continue; now=i+1&1,last=i&1; if(f[now][j][k]>f[last][j][k]+c[r[i]][r[i+1]]) f[now][j][k]=f[last][j][k]+c[r[i]][r[i+1]]; if(f[now][r[i]][k]>f[last][j][k]+c[j][r[i+1]]) f[now][r[i]][k]=f[last][j][k]+c[j][r[i+1]]; if(f[now][j][r[i]]>f[last][j][k]+c[k][r[i+1]]) f[now][j][r[i]]=f[last][j][k]+c[k][r[i+1]]; } } for(int i=1;i<=l;i++) for(int j=1;j<=l;j++) if(i!=j&&r[n]!=i&&r[n]!=j) ans=min(ans,f[n&1][i][j]); printf("%d\n",ans); return 0; }