hdu 3045 Picnic Cows(斜率优化DP)

时间:2022-02-02 15:48:20

题目链接:hdu 3045 Picnic Cows

题意:

  1. 有n个奶牛分别有对应的兴趣值,现在对奶牛分组,每组成员不少于t,
  2. 在每组中所有的成员兴趣值要减少到一致,问总共最少需要减少的兴趣值是多少。

题解:

  1. 分析:
    先对n个数进行排序,则可以分析出分组成员一定是连续的
    dp[i]表示前i个数得到的最少值
    则:从j~i作为一组
    dp[i]=dp[j-1]+sum[i]-sum[j-1]-(i-j+1)*s[j];//sum[i]表示前i个数的和
    =>dp[i]=dp[j-1]+sum[i]-sum[j-1]+(j-1)*s[j]-i*s[j];
    由于有i*s[j]这一项,所以无法直接在扫描数组的过程中用单调队列维护:
    dp[j-1]-sum[j-1]+(j-1)*s[j]-i*s[j]的最小值。
    考虑用斜率dp!
    假定k<j<=i-t以j~i作为一组比以k~i作为一组更优
    则:
    dp[j-1]+sum[i]-sum[j-1]-(i-j+1)*s[j] <= dp[k-1]+sum[i]-sum[k-1]-(i-k+1)*s[k]
    =>dp[j-1]+sum[i]-sum[j-1]+(j-1)*s[j]-i*s[j] <= dp[k-1]+sum[i]-sum[k-1]+(k-1)*s[k]-i*s[k]
    =>(dp[j-1]-sum[j-1]+(j-1)*s[j] - (dp[k-1]-sum[k-1]+(k-1)*s[k]))/(s[j]-s[k])<=i;//保证s[j]>=s[k]
    令:
    y1 = dp[j-1]-sum[j-1]+(j-1)*s[j]
    y2 = dp[k-1]-sum[k-1]+(k-1)*s[k]
    x1 = s[j]
    x2 = s[k]
    所以变成了:
    (y1 - y2)/(x1 - x2) <= i;
    斜率!
    只需要维护这个斜率即可

以上转自stephen博客

 #include<bits/stdc++.h>
#define F(i,a,b) for(int i=a;i<=b;++i)
using namespace std;
typedef long long ll; const int N=5e5+;
int n,t,Q[N];
ll sum[N],s[N],dp[N]; ll get_y(int j,int k)
{
return dp[j-]-sum[j-]+(j-)*s[j]-(dp[k-]-sum[k-]+(k-)*s[k]);
} ll get_x(int j,int k){return s[j]-s[k];} int check(int i,int j,int k)//获取更优的点
{
return get_y(i,j)*get_x(j,k)<=get_y(j,k)*get_x(i,j);
} int main()
{
while(~scanf("%d%d",&n,&t))
{
F(i,,n)scanf("%lld",s+i);
sort(s+,s++n);
F(i,,n)sum[i]=sum[i-]+s[i];
int en=*t-;
F(i,t,en)dp[i]=sum[i]-i*s[];//从t到2*t-1都只能分到一组里
int head=,tail=,st=*t;
F(i,st,n)
{
while(head<tail&&check(i-t+,Q[tail],Q[tail-]))tail--;
Q[++tail]=i-t+;
while(head<tail&&get_y(Q[head+],Q[head])<=get_x(Q[head+],Q[head])*i)head++;
dp[i]=dp[Q[head]-]+sum[i]-sum[Q[head]-]-(i-Q[head]+)*s[Q[head]];
}
printf("%lld\n",dp[n]);
}
return ;
}