HDU--2829--Lawrence--斜率优化做法

嗯,我是看别人的题解做,复述一下思路有助于强化记忆(* ̄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();
    }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值