题意
背景故事大概是说一战时一个英国间谍Lawrence要指挥突击队去炸毁奥斯曼帝国的一些铁轨。铁轨上有n个火车站,按线性排列,每个火车站上有一个权值,最后整个铁路的价值就是每一段联通铁路段上的火车站权值两两相乘的和加起来(联通段上只有一个车站就是0)。Lawrence有m次炸毁两个车站之间铁路的机会,求Lawrence最终可以使得这段铁路价值变成的最小值。
分析
转移方程易想出,设状态dp[i][j]为对于前i个车站的铁路来说,炸j次,能得到的最低价值,那么转移方程:
其中prod[a][b]值a到b间两两相乘的和。至于prod[a][b]的算法:
其中sum为前缀和,sq为前缀平方和。
那么代入转移方程中,移一下项,可以斜率优化,也可以四边形优化。
注意这是二维的斜率优化,要注意更新的是哪个j对应的队列。
AC代码
//HDU-2829 Lawrence
//AC 2017-2-26 15:10:03
//DP, slope trick
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1050;
int n,m;
long long sum[maxn],sq[maxn];
long long dp[maxn][maxn];
long long que[maxn][maxn];
int head[maxn],tail[maxn];
void init()
{
sum[0]=sq[0]=0;
for(int i=0;i<=m;++i)
tail[i]=head[i]=0;
return;
}
int que_size(int j)
{
return tail[j]-head[j];
}
int get_front(int j)
{
return que[j][head[j]];
}
long long gety(int i,int j)
{
return sum[i]*sum[i]+sq[i]+2*dp[i][j];
}
long long getx(int j)
{
return sum[j];
}
int slope_1(int a,int b,int c,int j)
{
return gety(b,j)-gety(a,j)<=2*sum[c]*(getx(b)-getx(a));
}
int slope_2(int a,int b,int c,int j)
{
return (gety(b,j)-gety(a,j))*(getx(c)-getx(b))>
(gety(c,j)-gety(b,j))*(getx(b)-getx(a));
}
void trail_head(int i,int j)
{
while(que_size(j)>=2)
{
if(slope_1(que[j][head[j]],que[j][head[j]+1],i,j))
++head[j];
else
break;
}
return;
}
void que_insert(int i,int j)
{
while(que_size(j)>=2)
{
if(slope_2(que[j][tail[j]-2],que[j][tail[j]-1],i,j))
--tail[j];
else
break;
}
que[j][tail[j]++]=i;
return;
}
int main()
{
while(scanf("%d %d",&n,&m)!=EOF&&n+m)
{
init();
for(int i=1;i<=n;++i)
{
scanf("%lld",sum+i);
sq[i]=sq[i-1]+sum[i]*sum[i];
sum[i]+=sum[i-1];
}
for(int i=1;i<=n;++i)
{
dp[i][0]=(sum[i]*sum[i]-sq[i])/2;
for(int j=1;j<=min(i-1,m);++j)
{
trail_head(i,j-1);
int k=get_front(j-1);
dp[i][j]=-2*sum[i]*sum[k]+gety(k,j-1)
+sum[i]*sum[i]-sq[i];
dp[i][j]/=2;
}
for(int j=0;j<=min(i-1,m);++j)
que_insert(i,j);
}
cout<<dp[n][m]<<endl;
}
return 0;
}