HDOJ 3045 Picnic Cows(斜率优化DP)

解决一个关于将一群牛按特定条件分组以最小化总体兴趣损失的问题,通过动态规划算法和斜率优化实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Picnic Cows

Time Limit: 8000/4000 MS(Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2880    Accepted Submission(s): 910

Problem Description

It’s summer vocation now.After tedious milking, cows are tired and wish to take a holiday. So FarmerCarolina considers having a picnic beside the river. But there is a problem,not all the cows consider it’s a good idea! Some cows like to swim in West Lake,some prefer to have a dinner in Shangri-la ,and others want to do somethingdifferent. But in order to manage expediently, Carolina coerces all cows tohave a picnic!
Farmer Carolina takes her N (1<N≤400000) cows to the destination, but shefinds every cow’s degree of interest in this activity is so different that theyall loss their interests. So she has to group them to different teams to makesure that every cow can go to a satisfied team. Considering about the security,she demands that there must be no less than T(1<T≤N)cows in every team. Asevery cow has its own interest degree of this picnic, we measure this interestdegree’s unit as “Moo~”. Cows in the same team should reduce their Moo~ to theone who has the lowest Moo~ in this team——It’s not a democratical action! SoCarolina wishes to minimize the TOTAL reduced Moo~s and groups N cows intoseveral teams.
For example, Carolina has 7 cows to picnic and their Moo~ are ‘8 5 6 2 1 7 6’and at least 3 cows in every team. So the best solution is that cow No.2,4,5 ina team (reduce (2-1)+(5-1) Moo~)and cow No.1,3,6,7 in a team (reduce((7-6)+(8-6)) Moo~),the answer is 8.

 

 

Input

The input contains multiplecases.
For each test case, the first line has two integer N, T indicates the number ofcows and amount of Safe-base line.
Following n numbers, describe the Moo~ of N cows , 1st is cow 1 , 2nd is cow 2,and so on.

 

 

Output

One line for each test case,containing one integer means the minimum of the TOTAL reduced Moo~s to group Ncows to several teams.

 

 

Sample Input

7 3

8 5 6 2 1 7 6

 

 

Sample Output

8


题意:给出n个数,要求把这些数分成若干组,每组至少有t个数,每组的reduce值为该组所有数依次减去这组最小的那个数的和,求所有组reduce值和的最小值

分析:肯定是要把小的数放在一起,大的数放在一起,故先对这些数进行排序

然后写出状态转移方程:dp[i]= min{dp[j]+(sum[i]-sum[j])-a[j+1](i-j)}  其中dp[i]表示前i个数分隔后得到的最小值

假设k<=j<i-t,j+1~i作为一组比以k+1~i作为一组更优

则dp[j]+sum[i]-sum[j]-(i-j)*a[j+1] <= dp[k]+sum[i]-sum[k]-(i-k)*a[k+1]

=>dp[j]+sum[i]-sum[j]+j*a[j+1]-i*a[j+1] <= dp[k]+sum[i]-sum[k]+k*a[k+1]-i*a[k+1]

=>(dp[j]-sum[j]+j*a[j+1] - (dp[k]-sum[k]+k*a[k+1]))/(a[j+1]-a[k+1])<=i

令yj=dp[j]-sum[j]+j*a[j+1],yk=dp[k]-sum[k]+k*a[k+1]);  xj=a[j+1], xk=a[k+1];

则表达式变为(yj-yk)/(xj-xk)<=i;左边为斜率的表示,维护斜率即可



#include <bits/stdc++.h>
using namespace std;
#define mst(a,b) memset((a),(b),sizeof(a))
#define f(i,a,b) for(int i=(a);i<=(b);++i)
const int maxn = 400005;
const int mod = 9973;
const int INF = 0x3f3f3f3f;
const double eps = 1e-6;
#define ll long long
#define rush() int T;scanf("%d",&T);while(T--)
int q[maxn];
ll a[maxn],sum[maxn];
ll dp[maxn];

ll getdp(int i,int j)
{
    return dp[j]+(sum[i]-sum[j])-a[j+1]*(i-j);
}

ll getup(int j,int k)
{
    return dp[j]-sum[j]+j*a[j+1]-(dp[k]-sum[k]+k*a[k+1]);
}

ll getdown(int j,int k)
{
    return a[j+1]-a[k+1];
}

int main()
{
    int n,t;
    int head,tail;
    while(~scanf("%d%d",&n,&t))
    {
        for(int i=1;i<=n;i++)
        {
            scanf("%I64d",&a[i]);
        }
        sort(a+1,a+n+1);
        sum[0]=dp[0]=0;
        for(int i=1;i<=n;i++)
        {
            sum[i]=sum[i-1]+a[i];
        }
        head=tail=0;
        q[tail++]=0;
        for(int i=1;i<=n;i++)
        {
            while(head+1<tail&&getup(q[head+1],q[head])<=i*getdown(q[head+1],q[head]))
                head++;
            dp[i]=getdp(i,q[head]);
            int cnt=i-t+1;
            if(cnt<t)
                continue;
            while(head+1<tail&&getup(cnt,q[tail-1])*getdown(q[tail-1],q[tail-2])<=getup(q[tail-1],q[tail-2])*getdown(cnt,q[tail-1]))
                tail--;
            q[tail++]=cnt;
        }
        printf("%I64d\n",dp[n]);
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值