摆渡车

题目描述

有 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);
}
  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值