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);
}