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;
}

浙公网安备 33010602011771号