7 3 8 5 6 2 1 7 6
8
/*题意: 有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; 斜率! 只需要维护这个斜率即可 */ #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <algorithm> #include <queue> #include <map> #include <cmath> #include <iomanip> #define INF 99999999 typedef __int64 LL; using namespace std; const int MAX = 400000 + 10; int n,t,q[MAX]; LL s[MAX],sum[MAX],dp[MAX]; LL GetY(int j,int k){ return dp[j-1]-sum[j-1]+(j-1)*s[j]-(dp[k-1]-sum[k-1]+(k-1)*s[k]); } LL GetX(int j,int k){ return s[j]-s[k]; } LL DP(){ int head=0,tail=1; q[0]=1; for(int i=1;i<=n;++i)sum[i]=sum[i-1]+s[i]; for(int i=t;i<2*t;++i)dp[i]=sum[i]-i*s[1];//初始化 for(int i=2*t;i<=n;++i){//i从2*t开始 while(head+1<tail && GetY(i-t+1,q[tail-1])*GetX(q[tail-1],q[tail-2])<=GetY(q[tail-1],q[tail-2])*GetX(i-t+1,q[tail-1]))--tail; q[tail++]=i-t+1; while(head+1<tail && GetY(q[head+1],q[head])<=GetX(q[head+1],q[head])*i)++head; dp[i]=dp[q[head]-1]+sum[i]-sum[q[head]-1]+(q[head]-1)*s[q[head]]-i*s[q[head]]; } return dp[n]; } int main(){ while(~scanf("%d%d",&n,&t)){ for(int i=1;i<=n;++i)scanf("%I64d",s+i);//cin>>s[i]; sort(s+1,s+1+n); printf("%I64d\n",DP()); } return 0; }
/*题意: 有n个奶牛分别有对应的兴趣值,现在对奶牛分组,每组成员不少于t, 在每组中所有的成员兴趣值要减少到一致,问总共最少需要减少的兴趣值是多少。 分析: 先对n个数进行排序,则可以分析出分组成员一定是连续的 dp[i]表示前i个数得到的最少值 则:从j+1~i作为一组 dp[i]=dp[j]+sum[i]-sum[j]-(i-j)*s[j+1];//sum[i]表示前i个数的和 =>dp[i]=dp[j]+sum[i]-sum[j]+j*s[j+1]-i*s[j+1]; 由于有i*s[j+1]这一项,所以无法直接在扫描数组的过程中用单调队列维护: dp[j]-sum[j]+j*s[j+1]-i*s[j+1]的最小值。 考虑用斜率dp! 假定k<=j<i-t以j+1~i作为一组比以k+1~i作为一组更优 则: dp[j]+sum[i]-sum[j]-(i-j)*s[j+1] <= dp[k]+sum[i]-sum[k]-(i-k)*s[k+1] =>dp[j]+sum[i]-sum[j]+j*s[j+1]-i*s[j+1] <= dp[k]+sum[i]-sum[k]+k*s[k+1]-i*s[k+1] =>(dp[j]-sum[j]+j*s[j+1] - (dp[k]-sum[k]+k*s[k+1]))/(s[j+1]-s[k+1])<=i;//保证s[j]>=s[k] 令: y1 = dp[j]-sum[j]+j*s[j+1] y2 = dp[k]-sum[k]+k*s[k+1] x1 = s[j+1] x2 = s[k+1] 所以变成了: (y1 - y2)/(x1 - x2) <= i; 斜率! 只需要维护这个斜率即可 */ #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <algorithm> #include <queue> #include <map> #include <cmath> #include <iomanip> #define INF 99999999 typedef __int64 LL; using namespace std; const int MAX = 400000 + 10; int n,t,q[MAX]; LL s[MAX],sum[MAX],dp[MAX]; LL GetY(int j,int k){ return dp[j]-sum[j]+j*s[j+1]-(dp[k]-sum[k]+k*s[k+1]); } LL GetX(int j,int k){ return s[j+1]-s[k+1]; } LL DP(){ int head=0,tail=1; q[0]=0; for(int i=1;i<=n;++i)sum[i]=sum[i-1]+s[i]; for(int i=t;i<2*t;++i)dp[i]=sum[i]-i*s[1];//初始化 for(int i=2*t;i<=n;++i){//i从2*t开始 int j=i-t; while(head+1<tail && GetY(j,q[tail-1])*GetX(q[tail-1],q[tail-2])<=GetY(q[tail-1],q[tail-2])*GetX(j,q[tail-1]))--tail; q[tail++]=j; while(head+1<tail && GetY(q[head+1],q[head])<=GetX(q[head+1],q[head])*i)++head; dp[i]=dp[q[head]]+sum[i]-sum[q[head]]+q[head]*s[q[head]+1]-i*s[q[head]+1]; } return dp[n]; } int main(){ while(~scanf("%d%d",&n,&t)){ for(int i=1;i<=n;++i)scanf("%I64d",s+i);//cin>>s[i]; sort(s+1,s+1+n); printf("%I64d\n",DP()); } return 0; }
/*题意: 有n个奶牛分别有对应的兴趣值,现在对奶牛分组,每组成员不少于t, 在每组中所有的成员兴趣值要减少到一致,问总共最少需要减少的兴趣值是多少。 分析: 先对n个数进行排序,则可以分析出分组成员一定是连续的 dp[i]表示前i个数得到的最少值 则:从j+1~i作为一组 dp[i]=dp[j]+sum[i]-sum[j]-(i-j)*s[j+1];//sum[i]表示前i个数的和 =>dp[i]=dp[j]+sum[i]-sum[j]+j*s[j+1]-i*s[j+1]; 由于有i*s[j+1]这一项,所以无法直接在扫描数组的过程中用单调队列维护: dp[j]-sum[j]+j*s[j+1]-i*s[j+1]的最小值。 考虑用斜率dp! 假定k<=j<i-t以j+1~i作为一组比以k+1~i作为一组更优 则: dp[j]+sum[i]-sum[j]-(i-j)*s[j+1] <= dp[k]+sum[i]-sum[k]-(i-k)*s[k+1] =>dp[j]+sum[i]-sum[j]+j*s[j+1]-i*s[j+1] <= dp[k]+sum[i]-sum[k]+k*s[k+1]-i*s[k+1] =>(dp[j]-sum[j]+j*s[j+1] - (dp[k]-sum[k]+k*s[k+1]))/(s[j+1]-s[k+1])<=i;//保证s[j]>=s[k] 令: y1 = dp[j]-sum[j]+j*s[j+1] y2 = dp[k]-sum[k]+k*s[k+1] x1 = s[j+1] x2 = s[k+1] 所以变成了: (y1 - y2)/(x1 - x2) <= i; 斜率! 只需要维护这个斜率即可 */ #include <iostream> #include <cstdio> #include <cstdlib> #include <cstring> #include <string> #include <algorithm> #include <queue> #include <map> #include <cmath> #include <iomanip> #define INF 99999999 typedef __int64 LL; using namespace std; const int MAX = 400000 + 10; int n,t,q[MAX]; LL s[MAX],sum[MAX],dp[MAX]; LL GetY(int j,int k){ return dp[j]-sum[j]+j*s[j+1]-(dp[k]-sum[k]+k*s[k+1]); } LL GetX(int j,int k){ return s[j+1]-s[k+1]; } LL DP(){ int head=0,tail=1; q[0]=0; for(int i=1;i<=n;++i)sum[i]=sum[i-1]+s[i]; //for(int i=t;i<2*t;++i)dp[i]=sum[i]-i*s[1];//初始化 for(int i=1;i<=n;++i){//i从2*t开始 while(head+1<tail && GetY(q[head+1],q[head])<=GetX(q[head+1],q[head])*i)++head; dp[i]=dp[q[head]]+sum[i]-sum[q[head]]+q[head]*s[q[head]+1]-i*s[q[head]+1]; if(i-t+1<t)continue; while(head+1<tail && GetY(i-t+1,q[tail-1])*GetX(q[tail-1],q[tail-2])<=GetY(q[tail-1],q[tail-2])*GetX(i-t+1,q[tail-1]))--tail; q[tail++]=i-t+1; } return dp[n]; } int main(){ while(~scanf("%d%d",&n,&t)){ for(int i=1;i<=n;++i)scanf("%I64d",s+i);//cin>>s[i]; sort(s+1,s+1+n); printf("%I64d\n",DP()); } return 0; }
原文:http://blog.csdn.net/xingyeyongheng/article/details/29597859