摆渡车—[2018 NOIP普及T3]——记忆化搜索

~目录~

        • 题目(2000ms)
        • 思路
        • Code


题目(2000ms)

描述
有n名同学要乘坐摆渡车从人大附中前往人民大学,第i位同学在第ti分钟去等车。只有一辆摆渡车在工作,但摆渡车容量可以视为无限大。摆渡车从人大附中出发、把车上的同学送到人民大学、再回到人大附中(去接其他同学),这样往返一趟总共花费m分钟(同学上下车时间忽略不计)。摆渡车要将所有同学都送到人民大学。 凯凯很好奇,如果他能任意安排摆渡车出发的时间,那么这些同学的等车时间之和最小为多少呢? 注意:摆渡车回到人大附中后可以即刻出发。
输入
第一行包含两个正整数n,m,以一个空格分开,分别代表等车人数和摆渡车往返一趟的时间。 第二行包含n个正整数,相邻两数之间以一个空格分隔,第i个非负整数ti代表第i个同学到达车站的时刻。
输出
输出一行,一个整数,表示所有同学等车时间之和的最小值(单位:分钟)。
范围
对于 10 % 10\% 10%的数据 , n ≤ 10 , m = 1 , 0 ≤ t i ≤ 100 。 ,n≤10,m=1,0≤ti≤100。 n10,m=1,0ti100
对于 30 % 30\% 30%的数据 , n ≤ 20 , m ≤ 2 , 0 ≤ t i ≤ 100 。 ,n≤20,m≤2,0≤ti≤100。 n20,m2,0ti100
对于 50 % 50\% 50%的数据 , n ≤ 500 , m ≤ 100 , 0 ≤ t i ≤ 1 0 4 。 ,n≤500,m≤100,0≤ti≤10^4。 n500,m100,0ti104
另有 20 % 20\% 20%的数据 , n ≤ 500 , m ≤ 10 , 0 ≤ t i ≤ 4 × 1 0 6 。 ,n≤500,m≤10,0≤ti≤4×10^6。 n500,m10,0ti4×106
对于 100 % 100\% 100%的数据 , n ≤ 500 , m ≤ 100 , 0 ≤ t i ≤ 4 × 1 0 6 ,n≤500,m≤100,0≤ti≤4×10^6 n500,m100,0ti4×106
样例

【样例输入15 1
3 4 4 3 5
【样例输出10

【样例输入25 5
11 13 1 5 5
【样例输出24

思路

都看的出来是DP吧。
关键就是怎么定义状态,当时我在考场推了很久,直接放弃,随手打了一个暴力,15分(真炸了)
可以知道的是,时间在本题中是一个比较重要的变量,所以我们DP中的一维就要用到时间,那到底是什么时间?是车的时间还是人的时间?其实都可以,但我觉得以人的时间可能会复杂一点点,因为我觉得如果车一一的遍历人,还不如人等车,一下就把所有人就都转移了

我定义的就是这般车开走的时间(因为即来即走,所以你说车回来的时间也可以),当前第 i i i个人,其实就意味着前 i − 1 i-1 i1个人已经处理了。那么对于车开走的时间,有的人小于等于这个时间,那么就需要等车;而有的人的时间大于这个时间,那么车就需要等人。
那么如何计算出等待的时间呢

  • 对于小于等于这个时间的t[i],我们画个图
    摆渡车—[2018 NOIP普及T3]——记忆化搜索_第1张图片
    对于每一个 t [ i ] t[i] t[i],我们可以发现等待时间就是,车开走的时间- t [ i ] t[i] t[i]的总和,
    也就是车开走的时间 ∗ * 小于车开走时间的 t [ i ] t[i] t[i]总数 − - 小于车开走的时间的 t [ i ] t[i] t[i]总和。那么下一班车的开始时间就是当前车开走的时间 + m +m +m
  • 对出大于车开走的时间的 t [ i ] t[i] t[i],我们也画个图
    摆渡车—[2018 NOIP普及T3]——记忆化搜索_第2张图片
    此时我们可以发现,现在与车开走的时间并没有多大的关系了,最重要的就是最大的 t [ j ] t[j] t[j],其实此时车开走的时间就是 t [ j ] t[j] t[j](车在等人),根据第一张图,我们就把 t [ j ] t[j] t[j]当做车开走的时间,根据上面的公式——车开走的时间 ∗ * 小于车开走时间的 t [ i ] t[i] t[i]总数 − - 小于车开走的时间的 t [ i ] t[i] t[i]总和。就可以的推出来了
    我用的记忆化搜索,自然从后往前填表

注意人等车和车等人的概念不要混淆了,
人等车指车还没来
车等人指车已经来了,在等人


Code

#include 
#include 
#include 
using namespace std;
#define INF 0x3f3f3f3f

int n, m;

int dp[505][505], a[505];

inline int calc(int i, int time_point){
	if( i == n+1 )
		return 0;
	if( a[i] > time_point )//如果等车的人,直接跳往下一个人来的时间
		return calc(i, a[i]); 
	if( dp[i][time_point-a[i]] )//记忆化
		return dp[i][time_point-a[i]];
	int sum = 0;
	int j = i;
	while( j <= n && a[j] <= time_point )//人等车
		sum += a[j++];
	int m_in = time_point*(j-i)-sum+calc(j, time_point+m);
	while( j <= n ){//车等人
		sum += a[j];
		m_in = min(m_in, a[j]*(j-i+1)-sum+calc(j+1, a[j]+m));
		j++;
	}
	return dp[i][time_point-a[i]] = m_in;
}

int main(){
	freopen("bus.in","r",stdin);
	freopen("bus.out","w",stdout);
    scanf("%d%d", &n, &m);
    for(int i = 1; i <= n; i ++){
    	scanf("%d", &a[i]);
	}
	sort(a+1, a+1+n);//排序
	printf("%d\n", calc(1, 0) );
	return 0;
}

你可能感兴趣的:(考试,NOIP,记忆化)