TYVJ1061 Mobile Service - DP - 滚动数组

考虑花费和员工的位置,请求的位置都有关,可以设状态f(i,x,y,z)表示完成了前i个请求,三个员工现在位于x,y,z三处的最小花费,由i可以方便地推到i+1
(DP既可以由i-1推到i,也可以是i推到i+1,灵活应用才能写好方程)

状态转移方程是:
fi+1,x,y,z=minfi+1,qi+1,y,z,fi,x,y,z+cx,qi+1 f ( i + 1 , x , y , z ) = m i n ( f ( i + 1 , q i + 1 , y , z ) , f ( i , x , y , z ) + c ( x , q i + 1 ) )
fi+1,x,y,z=minfi+1,x,qi+1,z,fi,x,y,z+cy,qi+1 f ( i + 1 , x , y , z ) = m i n ( f ( i + 1 , x , q i + 1 , z ) , f ( i , x , y , z ) + c ( y , q i + 1 ) )
fi+1,x,y,z=minfi+1,x,y,qi+1,fi,x,y,z+cz,qi+1 f ( i + 1 , x , y , z ) = m i n ( f ( i + 1 , x , y , q i + 1 ) , f ( i , x , y , z ) + c ( z , q i + 1 ) )
复杂度太高了,不过卡的好的话可以有60分~

但是我们发现员工的位置是根据请求而改变的,所以当第i个请求完成后,一定有一个员工位于位置 qi q i ,并且是哪个员工位于 qi q i 对答案是没有影响的

所以我们可以降低一维数组
fi,x,y f ( i , x , y ) 表示完成了前i个请求后,有一个员工位于 qi q i 处,另外两个员工位于x,y处的最小花费
q0 q 0 为3,这样就可以从i=0推到i=n

注意一点,对于状态转移方程: fi+1,x,qi=minfi+1,x,qi,fi,x,qi+cy,qi+1 f ( i + 1 , x , q i ) = m i n ( f ( i + 1 , x , q i ) , f ( i , x , q i ) + c ( y , q i + 1 ) )
表示,y位置的员工前往了 qi+1 q i + 1 位置,并且x,qi不动,所以i+1处的状态依然是x,qi 而不是 x, qi+1 q i + 1

但是这样还!不!够!

我们发现内存仍然不够用,需要再优化一维
考虑到第i个阶段只和第i-1阶段有关,我们可以开f[2][201][201]

设k=0,由k^1来变换k
并且在这样滚动的时候需要想好k和k^1所代表的含义
我设k为下一个请求,k^1为已完成的最后一个请求,那么递推就是由k^1推到k的,并且最终答案也是min(f[k][x][y])
在取最终答案时不能把f[k]和f[k^1]比较

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int MAXN = 100000 + 10; 
int c[210][210],l,n,que[1010],qi=3,ans=0x3f3f3f3f + 200;
int f[2][201][201];
int main() {
    scanf("%d %d", &l, &n); 
    for(int i=1; i<=l; i++)
        for(int j=1; j<=l; j++) 
            scanf("%d", &c[i][j]);
    for(int i=1; i<=n; i++)
        scanf("%d", que + i);
    memset(f,0x3f,sizeof(f));
    f[0][1][2] = 0;
    int k = 0;
    for(int i=0; i<n; i++) {
        int x, y;
        k^=1;
        for(int j=1; j<=l; j++)
            for(int t=1; t<=l; t++)
                f[k][j][t] = 0x3f3f3f3f;//在统计时要对即将要更新的阶段重新赋最大值
        for(int x=1; x<=l; x++)
            for(int y=1; y<=l; y++) {
                if(x==y || y==x) continue;
                f[k][x][y] = min(f[k][x][y],f[k^1][x][y] + c[qi][que[i+1]]);
                f[k][qi][y] = min(f[k][qi][y],f[k^1][x][y] + c[x][que[i+1]]);
                f[k][x][qi] = min(f[k][x][qi],f[k^1][x][y] + c[y][que[i+1]]);       
            }
        qi = que[i+1];
    }
    for(int x=1; x<=l; x++)
        for(int y=1; y<=l; y++)
            ans = min(ans, f[k][x][y]);
    printf("%d\n", ans);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值