参考了kuangbin大神的。
题意:大概就是给你n(1<=n<=1000)个数,
要你将其分成m + 1(0<=m<n)组,
要求每组数必须是连续的而且要求得到的价值最小。
一组数的价值定义为该组内任意两个数乘积之和,
如果某组中仅有一个数,那么该组数的价值为0。如:
将“4 5 1 2”分成一组得到的价值为:
4*5 + 4*1 + 4*2 + 5*1 + 5*2 + 1*2 = 49;
将“4 5 1 2”分成“4 5”和“1 2”两组得到的价值为:
4*5 + 1*2 = 22;
将“4 5 1 2”分成“4”和“5 1 2”两组得到的价值为:
0 + 5*1 + 5*2 + 1*2 = 17。
f[i][j]表示把第1~j元素分为i组两两乘积的最大
w[i]表示第i个元素的值
Sw[i]表示第1~i元素之和
Tw[i]表示第1~i元素的两两乘积
Sw[i]=Sw[i-1]+w[i];
Tw[i]=Tw[i-1]+Sw[i]*Sw[i-1];
f[i][j]=max(f[i-1][k]+cost(k+1,j)) i<=k<j
这种形式的dp方程可以考虑用斜率优化,需要验证其单调性
f[i][j]=max(f[i-1][k]+Tw[j]-Tw[k]-Sw[k]]*(Sw[j]-Sw[k]);
f[i][j]=max(f[i-1][k]+Tw[j]-Tw[k]-Sw[k]*Sw[j]-Sw[k]*Sw[k]);
令k1<k2;G(i,j,k)表示f[i][j]在枚举k状态时的检测值
G(i,j,k1)=f[i-1][k1]+Tw[j]-Tw[k1]-Sw[k1]*Sw[j]-Sw[k1]*Sw[k1]);
G(i,j,k2)=f[i-1][k2]+Tw[j]-Tw[k2]-Sw[k2]*Sw[j]-Sw[k2]*Sw[k2]);
G(i,j,k1)-G(i,j,k2)=f[i-1][k1]-T[k1]-Sw[k1]*Sw[j]+Sw[k1]*Sw[k1]-(f[i-1][k2]-T[k2]-Sw[k2]*Sw[j]+Sw[k2]*Sw[k2]);
打表或者其它方法可知G(i,j,k2)-G(i,j,k2)<0,
则
这说明G(i,j,k1)-G(i,j,k2)满足slope(k1,k2)>Sw[j],slope(p,q)表示(y[p]-y[q])/(x[p]-x[q]),在这里 x=Sw,y=f[]-Tw+Sw^2,slope(k1,k2)>Sw[j].意思就是对于f[i][j]的每个检测点两两斜率是一定大于Sw[j]。
G满足这样的规律有啥用呐?
设k1表示最优决策点,可知 slope(1~k1-1,k1),slope(k,k+1~j)>=Sw[j].
设k2表示对于f[i][j+1]的最优解。有slope(1~k2-1,k2),slope(k2,k2+1~j+1)>=Sw[j+1],而由条件给出的Sw[j+1]>Sw[j]。那么slope(k1,k2)>Sw[j+1]。
若再引入k3为f[i][j+2]的最优解。同理slope(k2,k3)>Sw[j+2]。则slope(k3,k2)>slope(k2,k1)
这样可以推出:决策点 f[i][j1],f[i][j2],f[i][j3],f[i][j4],j1<j2<j3<j4 他们的最优决策点k1,k2,k3,k4 满足slope(k1,k2)<slope(kl2,k3)<slope(k3,k4)
这样就可以对j这以一维度建立凸包,然后维持一个单调队列,使队列里,相邻两两间斜率递增,每个下凸性决策在进行第j决策时使用后即可将其与其之前的决策点抛弃。
所以每个元素的之只出入队列一次.时间效率从O(n)优化到O(1)。
/*题意:大概就是给你n(1<=n<=1000)个数,
要你将其分成m + 1(0<=m<n)组,
要求每组数必须是连续的而且要求得到的价值最小。
一组数的价值定义为该组内任意两个数乘积之和,
如果某组中仅有一个数,那么该组数的价值为0。如:
将“4 5 1 2”分成一组得到的价值为:
4*5 + 4*1 + 4*2 + 5*1 + 5*2 + 1*2 = 49;
将“4 5 1 2”分成“4 5”和“1 2”两组得到的价值为:
4*5 + 1*2 = 22;
将“4 5 1 2”分成“4”和“5 1 2”两组得到的价值为:
0 + 5*1 + 5*2 + 1*2 = 17。
*/
#include <stdio.h>
using namespace std;
const int maxn=1008;
int Sw[maxn],Tw[maxn],w[maxn];
int f[maxn][maxn];
int que[maxn],head,tail;
int n,m;
int dy(int i,int x2,int x1)
{
return f[i][x1]-Tw[x1]+Sw[x1]*Sw[x1]-(f[i][x2]-Tw[x2]+Sw[x2]*Sw[x2]);
}
int dx(int x2,int x1)
{
return Sw[x1] - Sw[x2];
}
void DP()
{
for(int i=1;i<=n;++i)
f[1][i]=Tw[i];
for(int i=2;i<=m+1;++i)
{
head=tail=0;
que[tail++]=i-1;
for(int j=i;j<=n;++j)
{
while(tail-head>=2)
{
int p=que[head],q=que[head+1];
if(dy(i-1,p,q)<Sw[j]*dx(p,q)) ++head;
else break;
}
f[i][j]=f[i-1][que[head]]+Tw[j]-Tw[que[head]]-Sw[que[head]]*(Sw[j]-Sw[que[head]]);
while (tail - head >= 2)
{
int p = que[tail-2], q = que[tail-1];
if(dy(i-1,p,q)*dx(q,j)>=dx(p,q)*dy(i-1,q,j)) --tail;
else break;
}
que[tail++]=j;
}
}
printf("%d\n",f[m+1][n]);
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
if(n==0&&m==0) break;
Sw[0]=Tw[0]=0;
for(int i=1;i<=n;++i)
{
scanf("%d",w+i);
Sw[i]=w[i]+Sw[i-1];
Tw[i]=Tw[i-1]+w[i]*Sw[i-1];
}
DP();
}
return 0;
}