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

  

posted @ 2019-10-21 16:07  EM-LGH  阅读(183)  评论(0编辑  收藏  举报