题意
有N只奶牛,每只奶牛有一个满意度,如果把一些奶牛分到一个组内,那么这些奶牛的满意度都会下降到组中满意度的最小值。现在规定每个组至少T只奶牛,求总的满意度变化的最小值
分析
从这个题中我学到了斜率DP中规定了转移距离的最小值时的处理方法(也就是i必须从小于等于i-T的状态转移而来)
状态不难想出,先对奶牛的满意度排个序,设dp[i]为前i只奶牛分好组后的满意度下降最小值,显然有:
其中j<=i-T
考虑到对当前状态i,i-T+1~i-1都不能作为状态转移的来源,他们此时也都不应该出现在单调队列中。于是与往常每算完一个状态的值就将其放入队列中不同,我们控制入队的时间,也即若当前为i,才令状态i-T+1入队(注意要+1,因为下一个位置是i+1)。当然同时也要判断i-T+1本身要大于T才能入队。
对应的处理也就是下面三行代码:
pre=i-T+1;
if(pre>=T) insert(pre);
AC代码
//HDU-3045 Picnic Cows
//AC 2017-3-11 16:20:17
//DP, slope trick
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=4e5+100;
int N,T;
long long dp[maxn],a[maxn];
long long sum[maxn];
int que[maxn],head,tail;
long long getx(int j)
{
return a[j+1];
}
long long gety(int j)
{
return dp[j]-sum[j]+j*a[j+1];
}
void insert(int j)
{
while(tail>head+1&&
(gety(j)-gety(que[tail-1]))*(getx(que[tail-1])-getx(que[tail-2]))<=
(gety(que[tail-1])-gety(que[tail-2]))*(getx(j)-getx(que[tail-1])))
--tail;
que[tail++]=j;
return;
}
int get_front(int i)
{
while(tail>head+1&&
(gety(que[head+1])-gety(que[head]))<=i*(getx(que[head+1])-getx(que[head])))
++head;
return que[head];
}
int main()
{
while(scanf("%d %d",&N,&T)!=EOF)
{
a[0]=dp[0]=sum[0]=0;
for(int i=1;i<=N;++i)
scanf("%lld",a+i);
sort(a+1,a+1+N);
for(int i=1;i<=N;++i)
sum[i]=a[i]+sum[i-1];
head=tail=0;
insert(0);
for(int i=1;i<=N;++i)
{
int pre=get_front(i);
dp[i]=dp[pre]+sum[i]-sum[pre]+(pre-i)*a[pre+1];
pre=i-T+1;
if(pre>=T)
insert(pre);
}
printf("%lld
",dp[N]);
}
return 0;
}