嗯,我是看别人的题解做,复述一下思路有助于强化记忆(* ̄m ̄)
首先搞一个前缀和数组 sum[i] 你应该知道这是干嘛的,
然后就是一个关键的 w 数组,w[i] 表示前i个数两两相乘的和,这是根据题意来的。
然后咧,状态转移方程就是这样滴: dp[k][i] = min{dp[k-1][j]+val(j+1,i)|0<=j<i}
val(j+1,i)表示[j+1,i]区间内的数字两两相乘的和.
然后咧: val(j+1,i)=w[i]-w[j]-sum[j]*(sum[i]-sum[j]) PS:全角虽然看着很丑,但是字母符号间间隔比较大,看得比较清楚,见谅(=^‥^=)
然后咧,推导一个斜率公式,嗯,,既然都知道是斜率优化了,就应该知道朝哪个方向去推导公式了,这里就略了。
再然后咧,剩下的做法就是一个普通的斜率优化DP了,维护一个 斜率上升的单调队列即可~
貌似这个题目数据比较小,优化了一下IO,时间没有多少进步,倒是用了一个滚动数组优化,空间提升比较大
从下往上,第一个是没加空间和IO优化的,第二个是用滚动数组优化了一下空间,结果还是蛮不错的。第三个是滚动数组加IO优化。。嗯,贴前两个的代码吧
这是一个没有空间优化的代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cstdlib>
using namespace std;
typedef long long LL;
const int MAXN = 1010;
const int MAXM = 1010;
LL sum[MAXN],w[MAXN];
LL d[MAXM][MAXN],*dp;
int a[MAXN];
int n,m;
void debug()
{
cout<<"this is W array"<<endl;
for(int i=1;i<=n;i++)cout<<w[i]<<" ";cout<<endl;
cout<<"And this is sum aRray"<<endl;
for(int i=1;i<=n;i++)cout<<sum[i]<<" ";cout<<endl;
cout<<"This is dpArray"<<endl;
for(int i=1;i<=n;i++)cout<<dp[i]<<" ";cout<<endl;
}
int q[MAXN],front,rear;
LL get_up(int j,int i)
{
return dp[i]-w[i]+sum[i]*sum[i]-dp[j]+w[j]-sum[j]*sum[j];
}
LL get_down(int j,int i)
{
return sum[i]-sum[j];
}
LL get_dp(int j,int i)
{
//printf("j==%d i==%d\n",j,i);
LL res = dp[j] + w[i] -w[j] -sum[j]*(sum[i]-sum[j]);
//cout<<"res ---"<<res<<endl;
return res;
}
void solve()
{
sum[0]=0;
w[0]=0;
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+a[i];
w[i]=a[i]*sum[i-1]+w[i-1];
}
for(int i=0;i<=m;i++)d[i][0]=0;
for(int i=1;i<=n;i++)d[0][i]=w[i];
dp=d[0];
#define bug0
#ifdef bug
debug();
#endif
for(int k=1;k<=m;k++)
{
front=rear=0;
q[rear++]=0;
dp=d[k-1];
for(int i=1;i<=n;i++)
{
while(rear-front>1&&
(
get_up(q[front],q[front+1])<sum[i]*get_down(q[front],q[front+1])
)
)front++;
d[k][i] = get_dp(q[front],i);
while(rear-front>1&&(get_up(q[rear-2],q[rear-1])*get_down(q[rear-1],i))>=
(get_up(q[rear-1],i)*get_down(q[rear-2],q[rear-1])))rear--;
q[rear++] = i;
//printf("dp[%d][%d] -> %lld\n",k,i,dp[i]);
}
}
cout<<d[m][n]<<endl;
}
int main()
{
freopen("2829.txt","r",stdin);
while(scanf("%d%d",&n,&m))
{
if(n+m==0)return 0;
for(int i=1;i<=n;i++)scanf("%d",a+i);
solve();
}
}
加上滚动数组优化空间
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<map>
#include<cstdlib>
using namespace std;
typedef long long LL;
const int MAXN = 1010;
const int MAXM = 1010;
LL sum[MAXN],w[MAXN];
LL *d[2],*dp;
int a[MAXN];
int n,m;
int readInt()
{
int res = 0;
char c;
while(1)
{
c=getchar();
if(c>='0'&&c<='9')
{
res = c-'0';
while(1)
{
c=getchar();
if(c>='0'&&c<='9')
{
res=res*10+c-'0';
}
else return res;
}
}
}
}
void debug()
{
cout<<"this is W array"<<endl;
for(int i=1;i<=n;i++)cout<<w[i]<<" ";cout<<endl;
cout<<"And this is sum aRray"<<endl;
for(int i=1;i<=n;i++)cout<<sum[i]<<" ";cout<<endl;
cout<<"This is dpArray"<<endl;
for(int i=1;i<=n;i++)cout<<dp[i]<<" ";cout<<endl;
}
int q[MAXN],front,rear;
LL get_up(int j,int i)
{
return dp[i]-w[i]+sum[i]*sum[i]-dp[j]+w[j]-sum[j]*sum[j];
}
LL get_down(int j,int i)
{
return sum[i]-sum[j];
}
LL get_dp(int j,int i)
{
//printf("j==%d i==%d\n",j,i);
LL res = dp[j] + w[i] -w[j] -sum[j]*(sum[i]-sum[j]);
//cout<<"res ---"<<res<<endl;
return res;
}
void solve()
{
sum[0]=0;
w[0]=0;
for(int i=1;i<=n;i++)
{
sum[i]=sum[i-1]+a[i];
w[i]=a[i]*sum[i-1]+w[i-1];
}
d[0] = new LL[MAXN];
d[1] = new LL[MAXN];
for(int i=1;i<=n;i++)d[0][i]=w[i];
dp=d[0];
#define bug0
#ifdef bug
debug();
#endif
for(int k=1;k<=m;k++)
{
front=rear=0;
q[rear++]=0;
dp=d[0];
for(int i=1;i<=n;i++)
{
while(rear-front>1&&
(
get_up(q[front],q[front+1])<sum[i]*get_down(q[front],q[front+1])
)
)front++;
d[1][i] = get_dp(q[front],i);
while(rear-front>1&&(get_up(q[rear-2],q[rear-1])*get_down(q[rear-1],i))>=
(get_up(q[rear-1],i)*get_down(q[rear-2],q[rear-1])))rear--;
q[rear++] = i;
//printf("dp[%d][%d] -> %lld\n",k,i,dp[i]);
}
swap(d[0],d[1]);
}
cout<<d[0][n]<<endl;
}
int main()
{
freopen("2829.txt","r",stdin);
while(1)
{
n=readInt();m=readInt();
if(n+m==0)return 0;
for(int i=1;i<=n;i++)a[i]=readInt();
solve();
}
}