[机房测试] 出租车

Description

出租车有k个停靠点,这k个停靠点依次排成一条直线,从左到右编号为1到k。出租车一次最多带4个人。共有n个人正在等待乘车,每个人都想从某个停靠点,去往另一个停靠点,即每个人的出发点和目的点不尽相同。这n个人到达各自上车的停靠点的时间均不相同,我们按到达时间的前后依次给这n个人编号为1到n,即先到的编号小。出租车公司有一个规定,先到的人必须比后到的人先上车,当然下车顺序则任意。

出租车每次可以移动到相邻编号的停靠点,需要耗费一个单位的时间,每个人上车或者下车都需要耗费一个单位时间,出租车一开始为空,且位于1号停靠点,出租车最后停靠点任意,那么将则n个人送到目的地至少需要多少时间呢?(注:题目保证出租车到达时需要上车的人已到达出发点)

\(n\leq 2000,k\leq 10\)

Solution

不考虑车上的人的具体编号,只记录他们的终点,然后再记录当前位置,和已经上过车的人数。转移考虑枚举上车或者下客。注意到当车满载时,必有一个人要下车,我们略去这个中间状态,直接从没满载转移到另一种没满载的状态,就可以省掉一维。复杂度 \(O(nk^4)\)

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

const int M=11;
const int N=2e3+7;

int dp[N][M][M][M][M],n,K,u[N],v[N];

inline void Min(int &x,int y){x=min(x,y);}
inline int abs(int x){return x<0? -x:x;}

int dfs(int i,int now,int x,int y,int z){
    if(i>n&&((!x)&&(!y)&&(!z))) return 0;
    int &ret=dp[i][now][x][y][z];
    if(~ret) return ret; else ret=1<<30;
    // 下车,当且仅当非空
    if(x) Min(ret,dfs(i,x,0,y,z)+abs(now-x));
    if(y) Min(ret,dfs(i,y,x,0,z)+abs(now-y));
    if(z) Min(ret,dfs(i,z,x,y,0)+abs(now-z));
    // 载人,当且仅当有人可载 
    if(i>n) return ret;
    if(x&&y&&z){
        Min(ret,dfs(i+1,v[i],x,y,z)+abs(now-u[i])+abs(u[i]-v[i]));
        Min(ret,dfs(i+1,x,v[i],y,z)+abs(now-u[i])+abs(u[i]-x));
        Min(ret,dfs(i+1,y,x,v[i],z)+abs(now-u[i])+abs(u[i]-y));
        Min(ret,dfs(i+1,z,x,y,v[i])+abs(now-u[i])+abs(u[i]-z));
    }else{
        if(!x) Min(ret,dfs(i+1,u[i],v[i],y,z)+abs(now-u[i]));
        if(!y) Min(ret,dfs(i+1,u[i],x,v[i],z)+abs(now-u[i]));
        if(!z) Min(ret,dfs(i+1,u[i],x,y,v[i])+abs(now-u[i]));
    }
    return ret;
}

int main(){
    freopen("taxi.in","r",stdin);
    freopen("taxi.out","w",stdout);
    memset(dp,-1,sizeof(dp));
    scanf("%d%d",&n,&K);
    for(int i=1;i<=n;i++) 
        scanf("%d%d",&u[i],&v[i]);
    printf("%d",dfs(1,1,0,0,0)+2*n);
}
posted @ 2021-11-02 20:51  Kreap  阅读(130)  评论(0编辑  收藏  举报