Mobile Service

题目描述

一个公司有三个移动服务员,最初分别在位置1,2,3处。
如果某个位置(用一个整数表示)有一个请求,那么公司必须指派某名员工赶到那个地方去。某一时刻只有一个员工能移动,且不允许在同样的位置出现两个员工。从 p 到 q 移动一个员工,需要花费 c(p,q)。这个函数不一定对称,但保证 c(p,p)=0。
给出N个请求,请求发生的位置分别为 p_1~p_N。公司必须按顺序依次满足所有请求,目标是最小化公司花费,请你帮忙计算这个最小花费。N≤1000,位置是1~200的整数。

输入格式

第1行:两个空格分隔的整数M, N(3<=M<=200, 1<=N<=1000)。M是位置数;N是请求数。每个位置从1到M编号。

接下来M行:每行包含M个非负整数。第i+1行的第j个数表示c(i,j) ,并且它小于2000。

最后1行包含N个数,是请求列表。一开始三个服务员分别在位置1,2,3。

输出格式

一行:一个整数,表示最小服务花费。

输入输出样例

输入样例1:复制

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

输出样例1:复制

5
【耗时限制】1000ms 【内存限制】256MB


这一题呢,你需要先理解是什么意思才好写

首先,他是一个线性dp

这个毋庸置疑

那就上思路喽

按顺序完成所有任务,首先考虑按照任务划分阶段:

        定义状态:d(i)表示完成前i个任务所需要的最小花费。

        状态转移方程:对于第i个任务有3种选择,分别让员工1、2、3来完成。

                                d(i) = d(i-1)+min(cost(i)) 。

        cost(i)表示完成任务i所需的花费,如何计算?

        cost(i)的计算需要知道完成任务i-1后3名员工所在的位置,状态参数和状态值中都不包含该信息。

        加入位置信息,定义状态:d(i,j,k,l)表示完成任务i后3名员工所在位置分别为j,k,l时的最低花费。

        最终答案ans = min(d(n, j, k, l)); 状态维度太多,会超时和超空间!

        当完成任务i后,3名员工中必有一人所在位置为p[i],i可以同时代表一个位置,并且3名员工是相同的。

        定义状态:d(i, j, k)表示完成任务i后,3名员工所在位置分别为p[i], j, k时的最低花费。

        状态转移方程:从拉的角度考虑状态转移方程决策太多,不方便处理,尝试从推的角度进行状态转移,推的话必然是像下一个任务的位置转移,考虑d(i, j, k)可以更新哪些状态。

        任务i完成后肯定是要处理问题i+1了,处理问题i+1有3种可选方案(两个员工不能到达同一个位置):

        ① 从p[i]走到p[i+1]:d(i+1,j,k) = d(i,j,k) + c[p[i]][p[i+1]]; (p[i+1] != j && p[i+1]!=k);

        ② 从j走到p[i+1]:d(i+1,p[i],k) = d(i,j,k) + c[j][p[i+1]]; (p[i+1] != p[i] && p[i+1]!=k);

        ③ 从k走到p[i+1]:d(i+1,j,p[i]) = d(i,j,k) + c[k][p[i+1]]; (p[i+1] != p[i] && p[i+1]!=j);

讲完了之后,上代码吧

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm> 
using namespace std;
const int N=1000+5;
const int INF=0x7fffffff; 
int n, m;
int a[210][210],p[N],f[N][210][210];
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	    for(int j=1;j<=n;j++)
			scanf("%d",&a[i][j]);
	for(int i=1;i<=m;i++)
	   	scanf("%d",&p[i]);
	memset(f,0x7f,sizeof(f));
	f[0][1][2]=0,p[0]=3;
	for(int i=0;i<m;i++)
	    for(int x=1;x<=n;x++)
			for(int y=1;y<=n;y++){
			    int z=p[i];
			    if(f[i][x][y]==INF) continue; 
			    if (x!=p[i+1]&&y!=p[i+1])
				f[i+1][x][y]=min(f[i+1][x][y],f[i][x][y]+a[z][p[i+1]]);
			    if(z!=p[i+1]&&x!=p[i+1])
				f[i+1][x][z]=min(f[i+1][x][z],f[i][x][y]+a[y][p[i+1]]);
			    if(y!=p[i+1]&&z!=p[i+1])
				f[i+1][y][z]=min(f[i+1][y][z],f[i][x][y]+a[x][p[i+1]]);
			}
	int ans=0x7f7f7f7f;
	for (int i=1;i<=n;i++)
	    for (int j=1;j<=n;j++)
			ans=min(ans,f[m][i][j]);
	printf("%d\n",ans);
    return 0;
}

  • 11
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值