题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5303
题意:长为L的环上有n个苹果树,环上0坐标在最上方,顺时针为坐标增加方向,给出每个苹果树的位置和数上的苹果数量,有容量为k的篮子,求摘完所有苹果的最小路程。
解析:摘苹果路线有3种情况:1、左边去摘,然后按原路返回 ;2、右边去摘,然后按原路返回;3、直接走一圈;
直接走一圈的情况自适用于两侧都还有苹果且可以一次装完且最后的苹果都离起点比较远的情况,而且最多只走一圈。我们先对左右贪心,然后枚举走一圈的情况找最小值。
由于苹果总数量<=1e5,于是我们可以基于每个苹果分析,而不是基于每个苹果树分析,方法是先求出每个苹果的坐标然后对苹果划分到左右两边,然后求左和右单侧装了i个苹果的最小路程disl[i和disr[i],利用这两个信息枚举最后走一圈时装的苹果的不同分布情况的最优值。
代码:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll maxN=1e5+5;int L,n,K;
ll x[maxN],disl[maxN],disr[maxN];
vector< ll > l,r;int main()
{int T;ll pos,num;scanf("%d",&T);while(T--){scanf("%d%d%d",&L,&n,&K);int cnt=1;for(int i=1;i<=n;i++){scanf("%I64d%I64d",&pos,&num);while(num--) x[cnt++]=pos;//x[i]即是第i个苹果的位置}cnt--;memset(disl,0,sizeof(disl));memset(disr,0,sizeof(disr));l.clear();r.clear();for(int i=1;i<=cnt;i++)//l和r数组是左边和右边的苹果{if((x[i]<<1)<L) l.push_back(x[i]);else r.push_back(L-x[i]);}sort(l.begin(),l.end());sort(r.begin(),r.end());int numl=l.size();int numr=r.size();for(int i=1;i<=numl;i++)//disl[i]是左侧装了i个苹果的最小路程disl[i]= i<=K ? l[i-1] : disl[i-K]+l[i-1];//左侧装i个苹果时,如果还装不满路程就是最远的苹果的路程,否则最小就是将后面k个装一篮的情况for(int i=1;i<=numr;i++)disr[i]= i<=K ? r[i-1] : disr[i-K]+r[i-1];ll ans=(disl[numl]+disr[numr])<<1;//假设全部从两边取for(int i=0;i<=numl&&i<=K;i++){int tl=numl-i; //左边留下i个用于绕圈取int tr=max(0,numr-(K-i));//右边留下(K-i)ans=min(ans,(disl[tl]+disr[tr])*2 + L);}printf("%I64d\n",ans);}return 0;
}