HDU-3045 Picnic Cows 【DP+斜率优化】

题目链接

题意

有N只奶牛,每只奶牛有一个满意度,如果把一些奶牛分到一个组内,那么这些奶牛的满意度都会下降到组中满意度的最小值。现在规定每个组至少T只奶牛,求总的满意度变化的最小值

分析

从这个题中我学到了斜率DP中规定了转移距离的最小值时的处理方法(也就是i必须从小于等于i-T的状态转移而来)
状态不难想出,先对奶牛的满意度排个序,设dp[i]为前i只奶牛分好组后的满意度下降最小值,显然有:

dp[i]=dp[j]+sum[i]sum[j]a[j+1](ij)

其中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;
}
原文地址:https://www.cnblogs.com/DrCarlluo/p/6580568.html