(纪中)1327. Mobile Service【DP】

66 篇文章 1 订阅

在这里插入图片描述


解题思路

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

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

考虑优化
由于我们可以确定在进行第i个任务时,第i-1个任务已经完成,也就是说肯定有一个人在p[i-1]这个位置上,根据这个特性,我们可以优化空间:
f [ i ] [ x ] [ y ] f [ i ] [ x ] [ y ] f[i][x][y] 表 示 完 成 了 前 i 个 请 求 , 其 他 两 个 员 工 位 于 x , y 位 置 的 最 小 花 费 f[i][x][y] 表示完成了前i个请求,其他两个员工位于 x,y 位置的最小花费f[i][x][y]表示完成了前i个请求,其他两个员工位于 x , y x,y x,y位置的最小花费

根据第三个人的位置是 p [ i − 1 ] p[i-1] p[i1],我们可以列出一下方程:

  • f [ o k ] [ j ] [ k ] = f [ o k ] [ k ] [ j ] = m i n ( f [ o k ] [ j ] [ k ] , f [ o k 1 ] [ j ] [ k ] + a [ p [ i − 1 ] ] [ p [ i ] ] ) ; f[ok][j][k]=f[ok][k][j]=min(f[ok][j][k],f[ok^1][j][k]+a[p[i-1]][p[i]]); f[ok][j][k]=f[ok][k][j]=min(f[ok][j][k],f[ok1][j][k]+a[p[i1]][p[i]]);
  • f [ o k ] [ p [ i − 1 ] ] [ k ] = f [ o k ] [ k ] [ p [ i − 1 ] ] = m i n ( f [ o k ] [ k ] [ p [ i − 1 ] ] , f [ o k 1 ] [ j ] [ k ] + a [ j ] [ p [ i ] ] ) ; f[ok][p[i-1]][k]=f[ok][k][p[i-1]]=min(f[ok][k][p[i-1]],f[ok^1][j][k]+a[j][p[i]]); f[ok][p[i1]][k]=f[ok][k][p[i1]]=min(f[ok][k][p[i1]],f[ok1][j][k]+a[j][p[i]]);
  • f [ o k ] [ p [ i − 1 ] ] [ j ] = f [ o k ] [ j ] [ p [ i − 1 ] ] = m i n ( f [ o k ] [ j ] [ p [ i − 1 ] ] , f [ o k 1 ] [ j ] [ k ] + a [ k ] [ p [ i ] ] ) ; f[ok][p[i-1]][j]=f[ok][j][p[i-1]]=min(f[ok][j][p[i-1]],f[ok^1][j][k]+a[k][p[i]]); f[ok][p[i1]][j]=f[ok][j][p[i1]]=min(f[ok][j][p[i1]],f[ok1][j][k]+a[k][p[i]]);

考虑到数组范围会MLE,开个滚动数组就好了


代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<iomanip>
#include<cstring>
#include<cmath>
#include<map>
#include<queue>
#define ll  long long
#define ldb long double
using namespace std;

int l,n,ans,a[210][210],p[1010],f[2][210][210];
bool ok;

int main(){
	scanf("%d%d",&l,&n);
	for(int i=1;i<=l;i++)
		for(int j=1;j<=l;j++)
			scanf("%d",&a[i][j]);
	for(int i=1;i<=n;i++)
		scanf("%d",&p[i]);
	memset(f,127,sizeof(f));
	p[0]=3;
	f[0][1][2]=0;
	ans=1e9;
	ok=0;
	for(int i=1;i<=n;i++)
	{
		ok=ok^1;
		for(int j=1;j<=l;j++)
		{
			for(int k=1;k<=l;k++)
			{
				if(p[i-1]==j||p[i-1]==k||j==k)continue;
				f[ok][j][k]=f[ok][k][j]=min(f[ok][j][k],f[ok^1][j][k]+a[p[i-1]][p[i]]);
				f[ok][p[i-1]][k]=f[ok][k][p[i-1]]=min(f[ok][k][p[i-1]],f[ok^1][j][k]+a[j][p[i]]);
				f[ok][p[i-1]][j]=f[ok][j][p[i-1]]=min(f[ok][j][p[i-1]],f[ok^1][j][k]+a[k][p[i]]);
				if(i==n)
					ans=min(ans,min(f[ok][j][k],min(f[ok][p[i-1]][j],f[ok][p[i-1]][k])));
			}
		}
		memset(f[ok^1],127,sizeof(f[ok^1]));
		
	}
	printf("%d",ans);
}
/*
4 10
0 5 0 8
8 0 5 6
1 8 0 6
1 1 4 0
1 1 1 1 4 4 2 2 2 3
*/
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值