【线性dp && 蓝书刷题】 Mobile Service

41 篇文章 0 订阅
5 篇文章 0 订阅

题目传送门

题目描述:

一个公司有三个移动服务员。如果某个地方有一个请求,某个员工必须赶到那个地方去(那个地方没有其他员工),某一时刻只有一个员工能移动。被请求后,他才能移动,不允许在同样的位置出现两个员工。从p到q移动一个员工,需要花费c(p,q)。这个函数没有必要对称,但是c(p,p)=0。公司必须满足所有的请求。目标是最小化公司花费。

c[i][j] 由题目输入给出


分析:

在第一反应下,我们不难想出以下状态:
f [ i ] [ x ] [ y ] [ z ] 表 示 完 成 了 前 i 个 请 求 , 三 个 员 工 分 别 位 于 x , y , z 位 置 的 最 小 花 费 f[i][x][y][z] 表示完成了前i个请求,三个员工分别位于 x,y,z 位置的最小花费 f[i][x][y][z]ix,y,z

我们可以考虑 用 阶段 i-1 来更新当前状态。
那么方程十分显然 ,这里不再说。

考虑优化
由于我们可以确定在进行第i个任务时,第i-1个任务已经完成,也就是说肯定有一个人在p[i-1]这个位置上,根据这个特性,我们可以优化空间:
f [ i ] [ x ] [ y ] 表 示 完 成 了 前 i 个 请 求 , 其 他 两 个 员 工 位 于 x , y 位 置 的 最 小 花 费 f[i][x][y] 表示完成了前i个请求,其他两个员工位于 x,y 位置的最小花费 f[i][x][y]ix,y
根据第三个人的位置是p[i-1],我们可以列出一下方程:
f [ i ] [ j ] [ k ] = f [ i ] [ k ] [ j ] = m i n ( f [ i ] [ j ] [ k ] , f [ i − 1 ] [ j ] [ k ] + c [ p [ i − 1 ] ] [ p [ i ] ] ) ; f[i][j][k]=f[i][k][j]=min(f[i][j][k],f[i-1][j][k]+c[p[i-1]][p[i]]); f[i][j][k]=f[i][k][j]=min(f[i][j][k],f[i1][j][k]+c[p[i1]][p[i]]);
f [ i ] [ p [ i − 1 ] ] [ j ] = f [ i ] [ j ] [ p [ i − 1 ] ] = m i n ( f [ i ] [ p [ i − 1 ] ] [ j ] , f [ i − 1 ] [ j ] [ k ] + c [ k ] [ p [ i ] ] ) ; f[i][p[i-1]][j]=f[i][j][p[i-1]]=min(f[i][p[i-1]][j],f[i-1][j][k]+c[k][p[i]]); f[i][p[i1]][j]=f[i][j][p[i1]]=min(f[i][p[i1]][j],f[i1][j][k]+c[k][p[i]]);
f [ i ] [ p [ i − 1 ] ] [ k ] = f [ i ] [ k ] [ p [ i − 1 ] ] = m i n ( f [ i ] [ p [ i − 1 ] ] [ k ] , f [ i − 1 ] [ j ] [ k ] + c [ j ] [ p [ i ] ] ) ; f[i][p[i-1]][k]=f[i][k][p[i-1]]=min(f[i][p[i-1]][k],f[i-1][j][k]+c[j][p[i]]); f[i][p[i1]][k]=f[i][k][p[i1]]=min(f[i][p[i1]][k],f[i1][j][k]+c[j][p[i]]);


Code

#include<bits/stdc++.h>
using namespace std;
int n,l;
int minn=1000000000;
int p[1010];
int c[1010][1010];
int f[1010][201][201];

int main(){
	freopen("test.in","r",stdin);
	freopen("test.out","w",stdout);
    scanf("%d %d",&l,&n);
    for (int i=1;i<=l;i++)
      for (int j=1;j<=l;j++)
        scanf("%d",&c[i][j]);
    p[0]=3;
    memset(f,20,sizeof(f));
    f[0][1][2]=0;
    for (int i=1;i<=n;i++) scanf("%d",&p[i]);
    for (int i=1;i<=n;i++){
	    for (int j=1;j<=200;j++)
	      for (int k=1;k<=200;k++){
		      if (j==k || j==p[i-1] || k==p[i-1]) continue;
		      f[i][j][k]=f[i][k][j]=min(f[i][j][k],f[i-1][j][k]+c[p[i-1]][p[i]]);
		      f[i][p[i-1]][j]=f[i][j][p[i-1]]=min(f[i][p[i-1]][j],f[i-1][j][k]+c[k][p[i]]);
		      f[i][p[i-1]][k]=f[i][k][p[i-1]]=min(f[i][p[i-1]][k],f[i-1][j][k]+c[j][p[i]]);
		      if (i==n) minn=min(minn,min(f[i][j][k],min(f[i][p[i-1]][j],f[i][p[i-1]][k])));
		  }
	}
	printf("%d",minn);
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值