题目链接:hdu 3045 Picnic Cows
题意:
- 有n个奶牛分别有对应的兴趣值,现在对奶牛分组,每组成员不少于t,
- 在每组中所有的成员兴趣值要减少到一致,问总共最少需要减少的兴趣值是多少。
题解:
- 分析:
先对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博客
1 #include<bits/stdc++.h> 2 #define F(i,a,b) for(int i=a;i<=b;++i) 3 using namespace std; 4 typedef long long ll; 5 6 const int N=5e5+7; 7 int n,t,Q[N]; 8 ll sum[N],s[N],dp[N]; 9 10 ll get_y(int j,int k) 11 { 12 return dp[j-1]-sum[j-1]+(j-1)*s[j]-(dp[k-1]-sum[k-1]+(k-1)*s[k]); 13 } 14 15 ll get_x(int j,int k){return s[j]-s[k];} 16 17 int check(int i,int j,int k)//获取更优的点 18 { 19 return get_y(i,j)*get_x(j,k)<=get_y(j,k)*get_x(i,j); 20 } 21 22 int main() 23 { 24 while(~scanf("%d%d",&n,&t)) 25 { 26 F(i,1,n)scanf("%lld",s+i); 27 sort(s+1,s+1+n); 28 F(i,1,n)sum[i]=sum[i-1]+s[i]; 29 int en=2*t-1; 30 F(i,t,en)dp[i]=sum[i]-i*s[1];//从t到2*t-1都只能分到一组里 31 int head=1,tail=0,st=2*t; 32 F(i,st,n) 33 { 34 while(head<tail&&check(i-t+1,Q[tail],Q[tail-1]))tail--; 35 Q[++tail]=i-t+1; 36 while(head<tail&&get_y(Q[head+1],Q[head])<=get_x(Q[head+1],Q[head])*i)head++; 37 dp[i]=dp[Q[head]-1]+sum[i]-sum[Q[head]-1]-(i-Q[head]+1)*s[Q[head]]; 38 } 39 printf("%lld\n",dp[n]); 40 } 41 return 0; 42 }
时间: 2024-10-11 06:46:24