算法竞赛进阶指南线性DP——移动服务

在这里插入图片描述
先来看,最终的目标是什么完成N个请求,且在第i个请求完成时,与前面的任务没有什么影响。

完成N个任务,跟谁有关,可以看到每一次有三名服务员,都可以派,具体派谁看谁去完成这个任务花费最少,花费与服务员上一轮所在的位置有关,则服务员的位置需要记录一下。

入此我们就能算出第i个任务结束,要完成第i+1个任务,派每个服务员所需产生的花费,都求出来找一个min即可。

Y以每一个任务为阶段
定义状态:f(i,x,y,z)完成前i个请求,服务员在x、y、z处所进行的花费。

考虑 F[i,x,y,z]能够更新i+1阶段的哪些状态。转移显然有三种,就是派三员工之一去第i+1个请求处。

  1. 派x服务员工作,则
    f(i+1,pi+1,y,z)=min( f(i+1,pi+1,y,z) , f(i,x,y,z)+c(x,pi+1) )

  2. 派y服务员工作,则
    f(i+1,x,pi+1,z)=min( f(i+1,x,pi+1,z) , f(i,x,y,z)+c(y,pi+1) )

  3. 派z服务员工作,则
    f(i+1,x,y,pi+1)=min( f(i+1,x,y,pi+1) , f(i,x,y,z)+c(z,pi+1) )

注意:要判断两个服务员不能出现在同一个地方的情况

时间复杂度 1000200200*200

优化
优化的方式可以考虑, 某一维是否可以用其他维度表示出来,如果可以,则这一维可以去掉

阶段不能去,三个服务员的位置,我们知道当第i个任务完成后,三个服务员中肯定有一个位置位于 pi 处。

就可以将完成第i个任务的人的位置为 pi,另外两个服务员的位置为x,y。

即 f(i,x,y) 为完成前i个请求,一个服务员位于 pi,另外两个分别位于x、y。

  1. 派位置位于 pi 的人去干活
    f( i+1, x,y)=min( f(i+1,x,y) , f(i,x,y)+c(pi,pi+1)
  2. 派位置位于 x的人去干活
    f( i+1, pi,y)=min( f(i+1,pi,y) , f(i,x,y)+c(x,pi+1)
  3. 派位置位于y的人去干活
    f( i+1, x,pi)=min( f(i+1,x,pi) , f(i,x,y)+c(y,pi+1)

^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
启示:
(1)先确定阶段,如果阶段不足以表示一个状态,则一些附加信息也可以作为附加维度。
(2)优化维度,可以判断某一个维度是否可以用其他维度表示出来,如完成上一轮任务的服务员的位置就在pi,减少枚举一个服务员的位置量。


const int N = 1010, M = 210;

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

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 >> q[i];

    memset(f, 0x3f, sizeof f);

    q[0] = 3;
    f[0][1][2] = 0;
    for (int i = 0; i < n; i++)
        for (int a = 1; a <= m; a++)
            for (int b = 1; b <= m; b++)
            {
            	//同一个位置不可 
                if (q[i] == a || q[i] == b || a == b) continue;
                int v = f[i][a][b];//上一个任务的值 
                int x = q[i], y = q[i + 1];
                //三种方案 
                f[i + 1][a][b] = min(f[i + 1][a][b], v + c[x][y]);
                f[i + 1][x][b] = min(f[i + 1][x][b], v + c[a][y]);
                f[i + 1][a][x] = min(f[i + 1][a][x], v + c[b][y]);
            }

    int res = 0x3f3f3f3f;
    for (int i = 1; i <= m; i++)
        for (int j = 1; j <= m; j++)
        {
            if (i == j || i == q[n] || j == q[n]) continue;
            res = min(res, f[n][i][j]);
        }
    cout << res << endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值