• NOIp2018普及组T3暨洛谷P5017 摆渡车:题解


    题目链接:https://www.luogu.org/problemnew/show/P5017

    emm,这次的真的不简单的,T3比T4难?

    醉了。。。

    蒟蒻肯定没有其他大佬讲的好啊,但肯定尽力,真的敲得呕心沥血,求过 。纪念写出的一道比较经典的线性动规。

    分析题意,我(以弱者的角度先看问题) 首先想到的是:排序+贪心。本以为今天如此简单,结果发现是自己太天真了。。。然而之后发现:并不一定要一次接着一次的发车,所以贪心破产。

    之后就有点摸不着头脑,去打了T4,出于宣泄直接上爆搜,惊奇的发现样例过了,赶快开心的回来再看T3.

    这时候就想:普及深搜,模拟,签到都出了,这道题多半就是动规了吧,于是,扯了这么一大堆下面进入正题。

    分析:

    思路:动规+前缀和(但据某些大佬说还可以用斜率优化?在这里很抱歉我太弱而不会)。

    首先我们直切核心——状态转移方程。

    我们可以设f[i]f[i]表示i时间前所有人的最小等待时间。

    PS:在这里如果不好确定方程的维数怎么办?可以结合数据范围和空间限制来考虑(多半是,虽然这题好像也可以有二维与t无关的数组)。

    我们可以把每个人的时间都标在一条时间轴上。然后能更直观的理解。

    这里借用@sooke 大佬的一张图。

    我们假设发车时间为每个来回4min,各位等车的同学如图中蓝点所示。

    然后我们假设当前要求的f[i]f[i]中的i=11i=11,于是下面开始分析。

    我们可以发现:如果之前的都已经算出的话,那么状态转移方程可如下所示:

    f[i]=min(f[i],f[j]+(cnt[i]cnt[j])i(sum[i]sum[j]));f[i]=min(f[i],f[j]+(cnt[i]-cnt[j])*i-(sum[i]-sum[j]));

    其中cnt[i]cnt[i]代表第i时间到达车站的同学的人数,sum[i]sum[i]代表第i时间到达车站的同学的时间的总和。

    jj即为上一辆车的发车时间。

    刚开始我们对于j的范围,应该能想到是:

    0<=j<=im0<=j<=i-m

    很明显的( ⊙ o ⊙ )!j的取值只要小于i并且和i相聚一个往返时间不就行了吗?

    然后的结果是:50分。(官方数据亲测)

    但是noip都结束了呀同志,我们不能只局限于50分呀!

    所以进行改进:

    这时候我们又想:可不可以将j的范围进一步缩小呢?

    但其实是肯定可以的。我们发现j可以:

    i2m+1<=j<=imi-2m+1<=j<=i-m

    为什么呢?因为如果两车间的相距时间大于了一趟往返的时间,那么我们完全可以在两者中间继续分割,并不影响原来的答案。

    这个时候也是大大的提升了程序的速度,然而:70分(官方数据亲测)

    泪奔~

    and then ,我们可以继续考虑有没有什么可以剪去的无用状态。

    仔细研究发现:当两次发车之间如果没有需要等待的同学的话,直接跳过即可。

    经过改正:100分(官方亲测)

    AC代码:

    #include<cstdio>
    #include<cmath>
    using namespace std;
    int a[501],cnt[4000005],sum[4000005],f[4000005];
    int main()
    {
    	int n,m;
    	scanf("%d%d",&n,&m);
    	int Time=0;
    	for(int i=1;i<=n;i++)
    	{
    		scanf("%d",&a[i]);
    		cnt[a[i]]++;
    		sum[a[i]]+=a[i];
    		Time=fmax(Time,a[i]);
    	}
    	for(int i=1;i<Time+m;i++)
    	{
    		cnt[i]+=cnt[i-1];
    		sum[i]+=sum[i-1];
    	}
    	for(int i=0;i<Time+m;i++)
    	{
    		if (i>=m&&cnt[i-m]==cnt[i])
    		{
    			 f[i]=f[i-m]; 
    			 continue; 
    		} 
    		f[i]=cnt[i]*i-sum[i];
    		int tmp;
    		tmp=fmax(i-2*m+1,0); 
    		for(int j=tmp;j<=i-m;j++)
    		{
    			f[i]=fmin(f[i],f[j]+(cnt[i]-cnt[j])*i-(sum[i]-sum[j]));
    		}
    	}
    	int ans=2147483647;
    	for(int i=Time;i<Time+m;i++)
    	{
    		ans=fmin(ans,f[i]);
    	}
    	printf("%d",ans);
    	return 0;
    }
    完结撒花~
    
  • 相关阅读:
    JAXB注解 @XmlRootElement 及XML文件解析详解
    JAXB 实现java对象与xml之间互相转换
    MyBatis之ResultMap标签
    如何通过<include/>标签重用Mybatis的代码段
    @Component, @Repository, @Service的区别
    @repository的含义,并且有时候却不用写,为什么?
    浪潮启动元脑生态计划,聚焦计算机视觉、量化交易等基础应用
    初生牛犊不怕虎 造车新势力的硬核移动互联科技盘点
    注意,有场景的公司正在拿起AI武器
    机器人运动大赛8月落地嘉峪关,编程和军事成亮点
  • 原文地址:https://www.cnblogs.com/ShineEternal/p/10834280.html
Copyright © 2020-2023  润新知