1 条题解
-
0
自动搬运
来自洛谷,原作者为

icaijy
**搬运于
2025-08-24 22:14:57,当前版本为作者最后更新于2024-12-21 22:35:12,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
这道题难点是想清楚最佳情况该怎么走。首先我们观察到要想走的越少,我们每次肯定会拿足物品,并且全部发放完才回来。这个很好理解,因为拿多一点物品并不会有什么代价,我们也不会还剩着东西就走回来,否则我们还得拿东西再走一趟这样得走更久。
第二是我们不会跳过人不给,因为跑到远处回来再补上跳过的人,和连续给完回来再跑到远处是一样的。以上总结一下就是每次我们会拿足 个纪念品并且分给连续的 个人,直到大家都有纪念品。
其次我们观察到大多数情况都是从哪出发,送完后从哪原路返回。而只有一种情况是会走一周回到原点,就是送完东西已经超过了中点了,这样原路返回就不如继续走完回来了。
走一周这个动作我们至多只会做一次。因为我们只会在送这 个纪念品时越过了中点,才会走一周。越过一次中点后中点两边的人都给了,所以就不可能再会过中点了。
假设我们会走一周。那么我们就枚举所有穿过中点的 个人。答案就是这 个人左右做送东西原路返回所需的时间,加上走一圈的时间。如图所示,假设红线为中点,底部为起点。我们枚举每个这样包含 个人的蓝色区间让他走一周即可。

假设我们不会走一周,那就更简单了,直接让中点左右都做送东西原路返回,答案是左右两边加起来。
实现的话可以使用动态规划。设 为以第 个元素作为送东西原路返回的终点所需的时间。若 在左半部分的话转移方程是 ,在右边则是 。注意我们并不关心每个区域有几个人,我们只关心人在哪个区域。
以下是代码:
#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; }
- 1
信息
- ID
- 4856
- 时间
- 1000ms
- 内存
- 125MiB
- 难度
- 3
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者