4 ACwing 274 Mobile Service 题解

Mobile Service

题面

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

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

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

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

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

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

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

\(3 \le L \le 200\)

\(1 \le N \le 1000\)

题解

因为公司必须按顺序满足所有请求,所以我们可以按照这些请求划分阶段,设 \(f(i,p1,p2,p3)\) 表示处理完前 \(i\) 个请求,三个人分别在 \(p1,p2,p3\) 的最小代价

如果这样 \(dp\) ,那么状态数是 \(O(NL^3)\) 级别的,是不可接受的

仔细观察发现,这三个位置中一定有一个位置在第 \(i\) 个请求的位置,所以我们可以只记两个位置,\(f(i,p1,p2)\) 表示完成前 \(i\) 个请求,有一个人在 \(i\) 请求的位置,另外两个人在 \(p1,p2\) 的最小代价,这样的状态数就是 \(O(NL^2)\) 的,大概 4e7 ,可以接受

转移

\[f(i + 1,x,y) = f(i,x,y) + c(p_i,p_{i + 1}) \\ f(i + 1,x, p_i) = f(i, x, y) + c(y,p{i + 1}) \\ f(i + 1, p_i, y) = f(i, x, y) + c(x, p_{i + 1}) \]

这道题给我们两点启发:

  1. 求解线性dp问题,一般先确定阶段,若阶段不足以表示一个状态,则可以把所需的附加信息也作为状态的维度

    在转移时,若总是从一个阶段转移到下一个阶段(本题 i -> i+1),则没有必要关心附加信息维度的大小变化情况(本题 x,y 在转移前后大小不定),因为无后效性已由阶段保证

  2. 在确定 dp 状态时,要选择最小的能够覆盖整个状态空间的 “维度集合”

    若 dp 状态由多个维度构成,则应检查维度之间能否相互导出,排除冗余维度

code

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>

using namespace std;

const int N = 1010, M = 210;

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

int main () {
    cin >> m >> n;
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> c[i][j];
        }
    }
    for (int i = 1; i <= n; i++) {
        cin >> p[i];
    }
    memset (f, 0x3f, sizeof f);
    
    f[0][1][2] = 0;
    
    //自己的初始化要符合定义
    p[0] = 3;
    
    for (int i = 0; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            for (int k = 1; k <= m; k++) {
                int x = p[i];
                
                //注意判断不合法状态
                if (x == j || x == k || j == k) continue;

                //可以在合适的地方用变量简化代码
                int v = f[i][j][k];
                f[i + 1][j][k] = min (f[i + 1][j][k], v + c[x][p[i + 1]]);
                f[i + 1][j][x] = min (f[i + 1][j][x], v + c[k][p[i + 1]]);
                f[i + 1][x][k] = min (f[i + 1][x][k], v + c[j][p[i + 1]]);
            }
        }
    }

    int ans = 2e9;
    for (int i = 1; i <= m; i++) {
        for (int j = 1; j <= m; j++) {
            ans = min (ans, f[n][i][j]);
        }
    }
    cout << ans << endl;


    return 0;
}
posted @ 2025-10-05 17:59  michaele  阅读(6)  评论(0)    收藏  举报