AcWing 274. 移动服务 (线性DP)

题意

一个公司有三个移动服务员,最初分别在位置 1,2,3处。

如果某个位置(用一个整数表示)有一个请求,那么公司必须指派某名员工赶到那个地方去。

某一时刻只有一个员工能移动,且不允许在同样的位置出现两个员工。

从 p 到 q 移动一个员工,需要花费 c(p,q)。

这个函数不一定对称,但保证 c(p,p)=0。

给出 N 个请求,请求发生的位置分别为 p1∼pN。

公司必须按顺序依次满足所有请求,且过程中不能去其他额外的位置,目标是最小化公司花费,请你帮忙计算这个最小花费。

思路

线性dp:

状态表示: f [ i ] [ x ] [ y ] f[i][x][y] f[i][x][y] 表示已经处理完前 i 个请求 并且服务员在 p[i]、x、y 位置上的所有状态的集合

**属性:**最小值

**状态计算:**由状态 i i i i + 1 i + 1 i+1转移比较方便 有三种情况

  1. 位于 p [ i ] p[i] p[i] 的服务员出发前往 p [ i + 1 ] p[i + 1] p[i+1] ,此时状态变成

    f [ i + 1 ] [ x ] [ y ] = m i n ( f [ i + 1 ] [ x ] [ y ] , f [ i ] [ x ] [ y ] + w [ p [ i ] ] [ p [ i + 1 ] ] ) f[i + 1][x][y] = min(f[i + 1][x][y],f[i][x][y] + w[p[i]][p[i + 1]]) f[i+1][x][y]=min(f[i+1][x][y],f[i][x][y]+w[p[i]][p[i+1]])

  2. 位于 x x x 的服务员出发前往 p [ i + 1 ] p[i + 1] p[i+1] ,此时状态变成

    f [ i + 1 ] [ p [ i ] ] [ y ] = m i n ( f [ i + 1 ] [ p [ i ] ] [ y ] , f [ i ] [ x ] [ y ] + w [ x ] [ p [ i + 1 ] ] ) f[i + 1][p[i]][y] = min(f[i + 1][p[i]][y],f[i][x][y] + w[x][p[i + 1]]) f[i+1][p[i]][y]=min(f[i+1][p[i]][y],f[i][x][y]+w[x][p[i+1]])

  3. 位于 y y y 的服务员出发前往 p [ i + 1 ] p[i + 1] p[i+1] ,此时状态变成

    f [ i + 1 ] [ x ] [ p [ i ] ] = m i n ( f [ i + 1 ] [ x ] [ p [ i ] ] , f [ i ] [ x ] [ y ] + w [ y ] [ p [ i + 1 ] ] ) f[i + 1][x][p[i]] = min(f[i + 1][x][p[i]],f[i][x][y] + w[y][p[i + 1]]) f[i+1][x][p[i]]=min(f[i+1][x][p[i]],f[i][x][y]+w[y][p[i+1]])

r e s = m i n ( f [ m ] [ i ] [ j ] ) i , j ∈ [ 1 , n ] res = min(f[m][i][j]) \quad \quad i,j \in [1,n] res=min(f[m][i][j])i,j[1,n]

这题首先肯定想到 用 f [ i ] [ x ] [ y ] [ z ] f[i][x][y][z] f[i][x][y][z] 表示状态 但是显然复杂度超了 所以考虑优化掉一维

由于不管怎么走 计算到 i + 1 i+1 i+1 时 肯定有一个服务员在 p [ i ] p[i] p[i] 所以只要枚举两个服务员的位置即可

不允许两个员工出现在同一个位置 所以要特判一下

代码

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define mod 998244353
#define endl '\n'
//#define int long long
using namespace std;
inline int gcd(int a, int b) { return b ? gcd(b, a%b) : a; }
inline int lowbit(int x) { return x & -x; }


typedef long long LL;
typedef pair<int, int>PII;
const int N = 210, M = 1010;

int n, m;
int w[N][N];
int f[M][N][N];
int p[M];

void solve() {
	cin >> n >> m;
	for (int i = 1; i <= n; ++i)
		for (int j = 1; j <= n; ++j)
			scanf("%lld", &w[i][j]);

	for (int i = 1; i <= m; ++i)
		cin >> p[i];

	memset(f, 0x3f, sizeof f);
	p[0] = 3;
	f[0][1][2] = 0;

	for (int i = 0; i < m; ++i) {
		for (int x = 1; x <= n; ++x) {
			for (int y = 1; y <= n; ++y) {
				if (x == y || x == p[i] || y == p[i])continue;

				f[i + 1][x][y] = min(f[i + 1][x][y], f[i][x][y] + w[p[i]][p[i + 1]]);
				f[i + 1][p[i]][y] = min(f[i + 1][p[i]][y], f[i][x][y] + w[x][p[i + 1]]);
				f[i + 1][x][p[i]] = min(f[i + 1][x][p[i]], f[i][x][y] + w[y][p[i + 1]]);
			}
		}
	}

	int res = INF;
	for (int x = 1; x <= n; ++x) {
		for (int y = 1; y <= n; ++y) {
			if (x == y || x == p[m] || y == p[m])continue;
			res = min(res, f[m][x][y]);
		}
	}

	cout << res << endl;
}

signed main() {
	//int t; cin >> t;
	//while(t--)
		solve();

	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zzqwtc

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值