果然蒟蒻只会普及组的题…… — — B y C h a l o t t o ——By\ \mathscr{Chalotto} ——By Chalotto
题目描述
有 n n n 名同学要乘坐摆渡车从人大附中前往人民大学,第 i i i 位同学在第 t i t_i ti 分钟去等车。只有一辆摆渡车在工作,摆渡车容量可以视为无限大。摆渡车从人大附中出发、把车上的同学送到人民大学、再回到人大附中(去接其他同学),这样往返一趟总共花费 m m m 分钟(同学上下车时间忽略不计)。摆渡车要将所有同学都送到人民大学。
凯凯很好奇,如果他能任意安排摆渡车出发的时间,那么这些同学的等车时间之和最小为多少呢?
注意:摆渡车回到人大附中后即刻出发。
输入格式
第一行包含两个正整数 n , m n,m n,m ,以一个空格分开,分别代表等车人数和摆渡车往返一趟的时间。
第二行包含 n n n 个正整数,相邻两数之间以一个空格分隔,第 i i i 个非负整数 t i t_i ti 代表第 i i i 个同学到达车站的时刻。
输出格式
输出一行,一个整数,表示所有同学等车时间之和的最小值(单位:分钟)。
样例输入1
5 1
3 4 4 3 5
样例输出1
0
样例说明
同学 1 1 1 和同学 4 4 4 在第 3 3 3 分钟开始等车,等待 0 0 0 分钟,在第 3 3 3 分钟乘坐摆渡车出发。摆渡车在第 4 4 4 分钟回答人大附中。
同学 2 2 2 和同学 3 3 3 在第 4 4 4 分钟开始等车,等待 0 0 0 分钟,在第 4 4 4 分钟乘坐摆渡车出发。摆渡车在第 5 5 5 分钟回到人大附中。
同学 5 5 5 在第 5 5 5 分钟开始等车,等待 0 0 0 分钟,在第 5 5 5 分钟乘坐摆渡车出发。自此所有同学都被送到人民大学。总等待时间为 0 0 0 。
样例输入2
5 5
11 13 1 5 5
样例输出2
4
样例说明
同学 3 3 3 在第 1 1 1 分钟开始等车,等待 0 0 0 分钟,在第 1 1 1 分钟乘坐摆渡车出发。摆渡车在第 6 6 6 分钟回答人大附中。
同学 4 4 4 和同学 5 5 5 在第 5 5 5 分钟开始等车,等待 1 1 1 分钟,在第 6 6 6 分钟乘坐摆渡车出发。摆渡车在第 11 11 11 分钟回到人大附中。
同学 1 1 1 在第 11 11 11 分钟开始等车,等待 2 2 2 分钟;同学 2 2 2 在第 13 13 13 分钟开始等车,等待 0 0 0 分钟。他/她们在第 13 13 13 分钟乘坐摆渡车出发。自此所有同学都被送到人民大学。总等待时间为 4 4 4 。可以证明,没有总等待时间小于 4 4 4 的方案。
数据规模与约定
对于 10 % 10\% 10% 的数据, n ≤ 10 , m = 1 , 0 ≤ t i ≤ 100 n\le 10,m=1,0\le t_i\le 100 n≤10,m=1,0≤ti≤100 。
对于 30 % 30\% 30% 的数据, n ≤ 20 , m ≤ 2 , 0 ≤ t i ≤ 100 n\le 20,m\le 2,0\le t_i\le 100 n≤20,m≤2,0≤ti≤100 。
对于 50 % 50\% 50% 的数据, n ≤ 500 , m ≤ 100 , 0 ≤ t i ≤ 1 0 4 n\le 500,m\le 100,0\le t_i\le 10^4 n≤500,m≤100,0≤ti≤104 。
另有 20 % 20\% 20% 的数据, n ≤ 500 , m ≤ 10 , 0 ≤ t i ≤ 4 × 1 0 6 n\le 500,m\le 10,0\le t_i\le 4\times 10^6 n≤500,m≤10,0≤ti≤4×106 。
对于 100 % 100\% 100% 的数据, n ≤ 500 , m ≤ 100 , 0 ≤ t i ≤ 4 × 1 0 6 n\le 500,m\le100,0\le t_i\le 4\times 10^6 n≤500,m≤100,0≤ti≤4×106 。
?解析?
显然证明法就是根据题目的意思进行直觉性判断,从而证明结论成立。
显然,这是一道 D P DP DP 题。
于是设 f [ i ] f[i] f[i] 表示第 i i i 分钟发出一辆车所要的最小时间,设 t t t 为最后一个人到达车站的时间。
得到状态转移方程: f [ i ] = m i n { f [ k ] + ∑ k < a [ j ] ≤ i i − a [ j ] } ( 0 ≤ k ≤ i − m ) f[i]=min\{f[k]+\sum_{kf[i]=min{f[k]+k<a[j]≤i∑i−a[j]}(0≤k≤i−m)其中 k k k 表示上一辆末班车回来的时间。
那么最后的答案即为: a n s = m i n { f [ i ] } ( t ≤ i < t + m ) ans=min\{f[i]\}(t\le i
i < t + m i
时间复杂度: Θ ( t 2 n ) \Theta(t^2n) Θ(t2n)
令 c n t [ i ] cnt[i] cnt[i] 表示到 i i i 为止到达车站的人数和
s u m [ i ] sum[i] sum[i] 表示到 i i i 为止到达车站的人的时间总和
显然有: ∑ k < a [ j ] ≤ i i − a [ j ] = i ∗ ( c n t [ i ] − c n t [ k ] ) − ( s u m [ i ] − s u m [ k ] ) \sum_{kk<a[j]≤i∑i−a[j]=i∗(cnt[i]−cnt[k])−(sum[i]−sum[k])即,总等待时间 = = = i × ( i ∼ k i\times (i\sim k i×(i∼k 内总人数 ) ) ) − - − 等待时间在 i ∼ k i\sim k i∼k 内所有人的等待时间之和。
附上一张图帮助理解:
于是方程变为: f [ i ] = m i n { f [ k ] + i ∗ ( c n t [ i ] − c n t [ k ] ) − ( s u m [ i ] − s u m [ k ] ) } ( k ≤ i − m ) f[i]=min\{f[k]+i*(cnt[i]-cnt[k])-(sum[i]-sum[k])\}(k\le i-m) f[i]=min{f[k]+i∗(cnt[i]−cnt[k])−(sum[i]−sum[k])}(k≤i−m)
时间复杂度: Θ ( t 2 ) \Theta(t^2) Θ(t2)
显然,没有一位同学的等待时间会超过 2 m 2m 2m 分钟。
简单证明:
考虑最坏的情况,在 k k k 时刻发了一辆车,有个同学在 k + 1 k+1 k+1 时到达车站,那么摆渡车将在 k + m k+m k+m 时返回,考虑到等待其它学生的情况,摆渡车最晚会在 k + 2 m − 1 k+2m-1 k+2m−1 时发车,否则还不如分别在 k + m k+m k+m 和 k + 2 m k+2m k+2m 的时候发出。
所以, k k k 的范围就变为: i − 2 m < i ≤ i − m i-2mi−2m<i≤i−m 。
时间复杂度: Θ ( t m ) \Theta(tm) Θ(tm)
完全不用虚了(́순◞౪◟순‵)
代码展示
#include
#define ll long long
#define me(x) memset(x,0,sizeof(x))
#define bi(x) memset(x,0x3f,sizeof(x))
#define ud using namespace std
ud;
const int maxn=1e7+10; int last_time=-9876543,ans=1<<30;
int N,M,t[maxn],f[maxn],sum[maxn]={},cnt[maxn]={};
inline long long read()
{
long long s=0,f=1; char ch; for(;ch<'0' || ch>'9';ch=getchar()) if(ch=='-') f=-1;
for(;ch>='0' && ch<='9';ch=getchar()) s=(s<<1)+(s<<3)+ch-'0'; return s*f;
}
int main()
{
N=read(),M=read();
for(int i=1;i<=N;++i)
{
t[i]=read();
// Find the last person who reach the station
last_time=max(last_time,t[i]);
++cnt[t[i]];//从0到i时间为止到达车站的人数和
sum[t[i]]+=t[i];//从0到i时间为止到达车站的人的时间总和
}
for(int i=1;i<last_time+M;++i)
{
cnt[i]+=cnt[i-1];
sum[i]+=sum[i-1];
}
for(int i=0;i<last_time+M;++i)
{
if(i>=M&&cnt[i-M]==cnt[i])
{
f[i]=f[i-M]; continue;
}
f[i]=cnt[i]*i-sum[i];
for(int j=max(i-(M<<1)+1,0);j<=i-M;++j)
f[i]=min(f[i],f[j]+(cnt[i]-cnt[j])*i-(sum[i]-sum[j]));
}
for(int i=last_time;i<last_time+M;++i) ans=min(ans,f[i]);
printf("%d\n",ans);
return 0;
}