题目大意:一个圈,上面有苹果树,每棵苹果树上面有若干苹果。给出圈的总长,以及每个苹果树的位置和上面的苹果数量,现在有一个最多可以装K个苹果的篮子,从位置0开始出发采摘苹果,问最少需要走多少距离,可以把所有苹果都运回起点0。
分析:
1、如果不是一个圈,而是一条线段,那么直接DP就好了。
2、将圈平分为左右两部分,如果在左边或者右边能够装满k个,或者某一边不足K个而另一边已经没有苹果了,此时出发采摘后按原路返回显然是最优的。
3、若在某一边不能装满K个,此时需比较原路返回与继续走到另一边采摘然后回到起点这两个方案谁更优。
4、由2、3可知,绕一圈的情况最多只能有一次。因为走一圈的前提是某一边的苹果数量不足K个,那么经过一圈后,该边的苹果必然已经全部被采摘完(否则方案显然不是最优的),此时只剩下另外一边有苹果。根据2,此时只需采摘完原路返回即可。因此最多只可能出现一次走一圈的情况。
综上得到以下解法:
1、先分左边和右边分别进行DP,求出最少距离。设dp1[i],dp2[i]分别表示采摘左边、右边的第i个苹果时的最少距离总和。
2、枚举走一圈情况下的左边和右边的苹果数量,得到距离和上面比较,取最小值。
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
typedef __int64 LL;
LL dp1[100005],dp2[100005];
int L[100005],R[100005];
int main()
{
int t,i,j,l,n,k;
cin>>t;
while(t--)
{
scanf("%d%d%d",&l,&n,&k);
int n1=0,n2=0;
for(i=1;i<=n;++i)
{
int x,m;
scanf("%d%d",&x,&m);
while(m--) {
if(2*x>l) L[++n1]=l-x;
else R[++n2]=x;
}
}
sort(L+1,L+1+n1);
sort(R+1,R+1+n2);
dp1[0]=dp2[0]=0;
for(i=1;i<=n1;++i){
if(i<=k) dp1[i]=L[i];
else dp1[i]=dp1[i-k]+L[i];
}
for(i=1;i<=n2;++i){
if(i<=k) dp2[i]=R[i];
else dp2[i]=dp2[i-k]+R[i];
}
LL ans=2*(dp1[n1]+dp2[n2]);
for(i=0;i<=min(n1,k);++i){ //枚举走一圈时左边苹果的数量
j=max(0,n2-(k-i)); //k-i为走一圈时右边苹果的数量,n2-(k-i)就是右边原路返回采摘的苹果数量
ans=min(ans,2*(dp1[n1-i]+dp2[j])+l);//最后的结果就是左边原路返回方案的花费加上右边原路返回的花费,再加上圈长。
}
printf("%I64d\n",ans);
}
return 0;
}