Mobile Service

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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值