NOIP201803摆渡车

题目链接:https://hdnoip.com/problem/P0313

分析

状态: dp[i]表示在i这个时刻,车在人大附中,最小的等车时间和
易得 状态转移方程为:
d p [ i ] = m i n ( d p [ j ] + [ j , i ] 时 刻 之 间 的 等 车 时 间 和 ) ( 0 < = j < i )

时间复杂度: O ( n^3 )

for (i: 1 ~ n) {  
    for (j: 1 ~ i - 1) 
	{  
        int cnt = 0;  
        for (k: j ~ i) 
		{  
            cnt += k时刻到来的人所需等待的时间   
        }  
        dp[i] = min (dp[i], dp[j] + cnt);  
    }  
}

ATTENTION:

最终的答案不在 d p [ m a x ( t [ i ] ) ] 里,因为将时间第二大的人送走后返回到人大附中的时间是

第 二 大 的 时 间 + m

所以答案应该为

m i n ( d p [ i ] ) 其中:m a x ( t [ j ] ) < = i < m a x ( t [ j ] ) + m

优化

1.我们先分析车,车的停留时间是不会超过m的(因为Ta可以在相同的状态下多跑一趟,这肯定比少跑一趟的结果优)

2.我们能不能用更快的时间去计算出[j, i]时刻之间的等车时间和呢?答案是可以的,我们可以用一个前缀和数组来实现,具体操作见下

for (int i = 1; i <= n; i++) 
{  
    scanf ("%lld", &a[i]);  
    r = Max (r, a[i]);  
    prenum[a[i]]++; pret[a[i]] += a[i];  
}  

for (int i = 1; i < r + m; i++) prenum[i] += prenum[i - 1], pret[i] += pret[i - 1];  
  
(prenum[i] - prenum[j]) * i - (pret[i] - pret[j])即为[j, i]时刻之间的等车时间和 

代码

#include <bits/stdc++.h>  
using namespace std;  
const int maxn=5000005;  
int t[maxn],num[maxn],st[maxn],f[maxn];int T=0;  
int main()  
{  
	int n,m;scanf("%d%d",&n,&m);  
	for(int i=1;i<=n;i++)  
	{  
		scanf("%d",&t[i]);
		num[t[i]]++;  
		T=max(T,t[i]);  
		st[t[i]]+=t[i];  
	}  
	for(int i=1;i<=T+m-1;i++)  num[i]+=num[i-1];st[i]+=st[i-1];  
	
	int minv=0x3f3f3f3f;  
	for(int i=1;i<=T+m-1;i++)  
	{  
		if(i>=m&&num[i-m]==num[i]) {f[i]=f[i-m];continue;}//中间没有点,直接转移即可   
		f[i]=num[i]*i-st[i];  
		for(int j=i-m;j>=max(0,i-2*m);j--)  
		{  
			f[i]=min(f[i],f[j]+(num[i]-num[j])*i-(st[i]-st[j]));  
		}  
	}  
	for(int i=T;i<=T+m-1;i++) minv=min(minv,f[i]);  
	printf("%d",minv);  
	return 0;  
}  

posted @ 2020-12-24 15:16  zshbolg  阅读(120)  评论(0)    收藏  举报