题解:P5849 [IOI2015] boxes
这道题难点是想清楚最佳情况该怎么走。首先我们观察到要想走的越少,我们每次肯定会拿足物品,并且全部发放完才回来。这个很好理解,因为拿多一点物品并不会有什么代价,我们也不会还剩着东西就走回来,否则我们还得拿东西再走一趟这样得走更久。
第二是我们不会跳过人不给,因为跑到远处回来再补上跳过的人,和连续给完回来再跑到远处是一样的。以上总结一下就是每次我们会拿足
其次我们观察到大多数情况都是从哪出发,送完后从哪原路返回。而只有一种情况是会走一周回到原点,就是送完东西已经超过了中点了,这样原路返回就不如继续走完回来了。
走一周这个动作我们至多只会做一次。因为我们只会在送这
假设我们会走一周。那么我们就枚举所有穿过中点的
假设我们不会走一周,那就更简单了,直接让中点左右都做送东西原路返回,答案是左右两边加起来。
实现的话可以使用动态规划。设
以下是代码:
#include <bits/stdc++.h>
#define int long long
using namespace std;
int n,k,l;
int a[10000005];
int dp[10000005];
bool right(int i){
return a[i]>=(l+1)/2;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cin>>n>>k>>l;
for (int i=1;i<=n;i++) cin>>a[i];
int b;
for (int i=1;;i++) {
if (right(i)) {
b=i;
break;
}
dp[i]=a[i]+dp[max(0ll,i-k)];
}
for (int i=n;right(i);i--) dp[i]=l-a[i]+dp[min(n+1,i+k)];
int ans=21000000000000000;
for (int i=1;i<=n;i++){
if (i+k-1<b) continue;
if (i+k-1>n||i>=b) break;
ans=min(ans,2*dp[i-1]+2*dp[i+k]+l);
}
cout<<min(ans,dp[b-1]*2+dp[b]*2);
return 0;
}