HDU 3045 picnic cows(斜率DP)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3045

题目大意:有n个数,可以把n个数分成若干组,每组不得小于m个数,每组的价值=除了该组最小值以外每个值-最小值之和,求使得所有组的价值之和的最小值。

解题思路:将n个数按从小到大排序,处理前i为前缀和为sum[i],则可得出状态转移方程:dp[i]=min{dp[j]+sum[i]-sum[j+1]-a[j+1]*(i-j-1)}(0<=j<i-m+1),再用斜率DP优化即可。

     注意:一定要判断j是否大于等于m,因为至少m才能算一组奶牛,不然会出错。

代码:

 1 #include<iostream>
 2 #include<algorithm>
 3 #include<cstring>
 4 #include<cstdio>
 5 using namespace std;
 6 typedef long long LL;
 7 const int N=5e5+5;
 8 
 9 int head,tail;
10 LL sum[N],dp[N],a[N],q[N];;
11 
12 LL getUP(int k,int j){
13     return dp[j]+a[j+1]*(j+1)-sum[j+1]-dp[k]-a[k+1]*(k+1)+sum[k+1];
14 }
15 
16 LL getDOWN(int k,int j){
17     return a[j+1]-a[k+1];
18 }
19 
20 //dp[i]=min{dp[j]+sum[i]-sum[j+1]-a[j+1]*(i-j-1)}
21 LL getDP(int i,int j){
22     return dp[j]+sum[i]-sum[j+1]-a[j+1]*(i-j-1);
23 }
24 
25 int main(){
26     int n,m;
27     while(~scanf("%d %d",&n,&m)){
28         memset(dp,0x3f3f3f3f,sizeof(dp));
29         for(int i=1;i<=n;i++){
30             scanf("%lld",&a[i]);
31         }
32         sort(a+1,a+1+n);
33         for(int i=1;i<=n;i++){
34             sum[i]=sum[i-1]+a[i];
35         }
36         dp[0]=0;
37         head=tail=0;
38         q[tail++]=0;
39         for(int i=1;i<=n;i++){
40             while(head+1<tail&&getUP(q[head],q[head+1])<=i*getDOWN(q[head],q[head+1])){
41                 head++;
42             }
43             dp[i]=getDP(i,q[head]);
44             int j=i-m+1;
45             //注意z这个判断,因为状态转移,也就是分组,至少要保证第一组有m头牛。 
46             if(j<m)
47                 continue;
48             while(head+1<tail&&getUP(q[tail-1],j)*getDOWN(q[tail-2],q[tail-1])<=getUP(q[tail-2],q[tail-1])*getDOWN(q[tail-1],j)){
49                 tail--;
50             }
51             q[tail++]=j;
52         }
53         printf("%lld
",dp[n]);
54     }
55 
56     return 0;
57 }
原文地址:https://www.cnblogs.com/fu3638/p/7850374.html