SP703 SERVICE_Mobile Service[动态规划]

S E R V I C E − M o b i l e − S e r v i c e SERVICE-Mobile-Service SERVICEMobileService


Description

链接


Solution

以三个员工的位置作为状态, 第 i i i次询问作为阶段
可以得出 D p Dp Dp 雏形
d p [ i , x , y , z ] dp[i, x, y, z] dp[i,x,y,z]表示完成第 i i i次询问, 且员工位置分别在 x , y , z x, y, z x,y,z时的最小花费
考虑其能够更新的状态 :

  1. d p [ i + 1 , Q i + 1 , y , z ] dp[i+1, Q_{i+1}, y, z] dp[i+1,Qi+1,y,z]
  2. d p [ i + 1 , x , Q i + 1 , z ] dp[i+1, x, Q_{i+1}, z] dp[i+1,x,Qi+1,z]
  3. d p [ i + 1 , x , y , Q i + 1 ] dp[i+1, x, y, Q_{i+1}] dp[i+1,x,y,Qi+1]

但是以此作为状态的时空复杂度为 O ( Q ∗ N 3 ) O(Q*N^3) O(QN3), 无论是空间和时间都无法承受 .
这时可以观察到, 当完成上一个询问时, 已经有一个员工位置确定下来了.
于是可以设 d p [ i , x , y ] dp[i, x, y] dp[i,x,y]表示完成第 i i i次询问, 且员工位置分别在 x , y , Q u e i − 1 x, y, Que_{i-1} x,y,Quei1时的最小花费
转移时同理 .


Attention

以从 x x x Q u e i Que_i Quei 位置为例

容易写成
d p [ i , Q u e i , y ] = m i n { d p [ i − 1 , x , y ] + c o [ x , Q u e i ] } dp[i, Que_{i}, y] = min\{ dp[i-1, x, y] + co[x, Que_i] \} dp[i,Quei,y]=min{dp[i1,x,y]+co[x,Quei]}
正确的却是
d p [ i , Q u e i − 1 , y ] = m i n { d p [ i − 1 , x , y ] + c o [ x , Q u e i ] } dp[i, Que_{i-1}, y] = min\{ dp[i-1, x, y] + co[x, Que_i] \} dp[i,Quei1,y]=min{dp[i1,x,y]+co[x,Quei]}

错误的原因就在于没有考虑所要更新的状态会去更新哪些状态
由于省略了一维, 在将一个员工 H 转移到新位置的时,
H 对于 i + 1 i+1 i+1询问来说, 相当于那个省略的一维,
那个在 i i i 询问中省略的 P员工, 对于 i + 1 i+1 i+1询问来说, 却是没有省略的那个员工,
所以转移新位置时将 P员工 放在已知的那个位置


Code

#include<bits/stdc++.h>
#define reg register

int read(){
        int s = 0, flag = 1;
        char c;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

int N, M;
int co[205][205];
int dp[1005][205][205];
int Que[1005];

void Work(){
        N = read(), M = read();
        for(reg int i = 1; i <= N; i ++)
                for(reg int j = 1; j <= N; j ++) co[i][j] = read();
        for(reg int i = 1; i <= M; i ++) Que[i] = read();
        memset(dp, 0x3f, sizeof dp);
        Que[0] = 3, dp[0][1][2] = 0;
        int Ans = 0x3f3f3f3f;
        for(reg int i = 1; i <= M; i ++)
                for(reg int x = 1; x <= N; x ++)
                        for(reg int y = 1; y <= N; y ++){
                                if(Que[i-1] == x || Que[i-1] == y || x == y) continue ;
                                if(Que[i] != y && Que[i] != Que[i-1]){
                                        int temp = dp[i][Que[i-1]][y] = std::min(dp[i][Que[i-1]][y], dp[i-1][x][y] + co[x][Que[i]]);
                                        if(i == M) Ans = std::min(Ans, temp);
                                }
                                if(Que[i] != x && Que[i] != Que[i-1]){
                                        int temp = dp[i][x][Que[i-1]] = std::min(dp[i][x][Que[i-1]], dp[i-1][x][y] + co[y][Que[i]]);
                                        if(i == M) Ans = std::min(Ans, temp);
                                }
                                if(Que[i] != x && Que[i] != y){
                                        int temp = dp[i][x][y] = std::min(dp[i][x][y], dp[i-1][x][y] + co[Que[i-1]][Que[i]]);
                                        if(i == M) Ans = std::min(Ans, temp);
                                }
                        }
        printf("%d\n", Ans);
}

int main(){
        int T = read();
        while(T --) Work();
        return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值