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