BZOJ 4368: [IOI2015]boxes纪念品盒 贪心
题意:给定一个环,环上有一些点包裹,你要从 $0$ 号点出发,然后每次带上一个容量为 $k$ 的背包.
问:如果要把所有的包裹都带回 $0$ 好点最少要走多少距离.
每一次只有 $3$ 种走法:走整圆,没走到半圆就返回(两个方向)
而我们可以贪心证明我们最多只会走一次整圆:
如果我们要走 $2$ 次或两次以上整圆的话说明要拿的物品肯定 $>2k$ 个.
而背包的容量是有限的:只能装 $k$ 个.
所以这么走是不如先装物品多的那一侧半圆,然后再跑一次整圆.
然后就好做了:直接对每一侧半圆求取前 $i$ 个的最小距离(显然如果取远的一定会顺带取近的)
#include <bits/stdc++.h> #define N 10000007 #define LL long long #define setIO(s) freopen(s".in","r",stdin) using namespace std; LL f1[N],f2[N]; int L,n,k,t1,t2,p[N],s1[N],s2[N]; int main() { // setIO("input"); int i,j; scanf("%d%d%d",&n,&k,&L); for(i=1;i<=n;++i) { scanf("%d",&p[i]); if(p[i]<=L/2) { s1[++t1]=p[i]; } else { s2[++t2]=L-p[i]; } } for(i=1;i<=t2/2;++i) swap(s2[i],s2[t2-i+1]); for(i=1;i<=t1;++i) { if(i<k) { f1[i]=1ll*s1[i]; } else { f1[i]=1ll*s1[i]+f1[i-k]; } } for(i=1;i<=t2;++i) { if(i<k) { f2[i]=1ll*s2[i]; } else{ f2[i]=f2[i-k]+1ll*s2[i]; } } LL ans=2ll*(f1[t1]+f2[t2]); for(i=max(t1-k,0);i<=t1;++i) { ans=min(ans, (LL)2ll*(f1[i]+f2[max(t2-k+t1-i,0)])+1ll*L); } printf("%lld\n",ans); return 0; }