题目描述
有 n 名同学要乘坐摆渡车从人大附中前往人民大学,第 i 位同学在第 ti分钟去等车。只有一辆摆渡车在工作,但摆渡车容量可以视为无限大。摆渡车从人大附中出发、 把车上的同学送到人民大学、再回到人大附中(去接其他同学),这样往返一趟总共花费m分钟(同学上下车时间忽略不计)。摆渡车要将所有同学都送到人民大学。
凯凯很好奇,如果他能任意安排摆渡车出发的时间,那么这些同学的等车时间之和最小为多少呢?
注意:摆渡车回到人大附中后可以即刻出发。
输入输出格式
输入格式
第一行包含两个正整数 n,m以一个空格分开,分别代表等车人数和摆渡车往返 一趟的时间。
第二行包含 n 个正整数,相邻两数之间以一个空格分隔,第 i 个非负整数 ti
代表第 i 个同学到达车站的时刻。
输出格式
输出一行,一个整数,表示所有同学等车时间之和的最小值(单位:分钟)。
输入输出样例
输入样例
输入样例1:
5 1
3 4 4 3 5
输入样例2
5 5
11 13 1 5 5
输出样例
输出样例1
0
输出样例2
4
题目分析
DP(废话)
问题是怎么DP?我们不妨先举只有两个人的例子:假设车辆往返一趟要5分钟,两个同学到站的时间分别是1和6或1和4,我们不难得到规律:如果后一个人到站时间-前一个人到站时间>=m,那么直接运走。转换成DP语言,就可以得出状态转移方程:
if((a[i]+j)-(a[i-1]+k)>=m)//不等人
f[i][j]=min(f[i][j],f[i-1][k]+j);
else if((a[i]+j)==(a[i-1]+k))//等人
f[i][j]=min(f[i][j],f[i-1][k]+j);
上面就是状态转移方程,那么f[i][j]和k表示什么?其实f[i][j]表示到第i个人的时候让他等j分钟的车的最小之和。k则表示如果等后面的人,那么就需要等k分钟。很容易得到:
for(ll i=2;i<=n;i++)//枚举每个人
for(ll j=0;j<=2*m-2;j++)//每个人等j分钟的车,最多(2*m-2)分钟
{
f[i][j]=2147483647;
for(ll k=0;k<=2*m-2;k++)//判断前面的要不要等后面的
{
if((a[i]+j)-(a[i-1]+k)>=m)//不等人
f[i][j]=min(f[i][j],f[i-1][k]+j);
else if((a[i]+j)==(a[i-1]+k))//等人
f[i][j]=min(f[i][j],f[i-1][k]+j);
}
}
当然,还有把每个人的等车时间初始化,其实也不难:
for(ll i=1;i<=n;i++)
f[1][i]=i;//初始化等车的时间
以上就是本题的核心代码,下面我双手奉上AC代码:
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N=2004;
ll a[N],f[N][N],n,m,ans;
int main()
{
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++)
scanf("%lld",&a[i]);
for(ll i=1;i<=n;i++)
f[1][i]=i;//初始化等车的时间
sort(a+1,a+n+1);//排序
for(ll i=2;i<=n;i++)//枚举每个人
for(ll j=0;j<=2*m-2;j++)//每个人等j分钟的车,最多(2*m-2)分钟
{
f[i][j]=2147483647;
for(ll k=0;k<=2*m-2;k++)//判断前面的要不要等后面的
{
if((a[i]+j)-(a[i-1]+k)>=m)//不等人
f[i][j]=min(f[i][j],f[i-1][k]+j);
else if((a[i]+j)==(a[i-1]+k))//等人
f[i][j]=min(f[i][j],f[i-1][k]+j);
}
}
ans=f[n][0];
for(ll i=1;i<=2*m-2;i++)
ans=min(ans,f[n][i]);
printf("%lld",ans);
}