zoukankan      html  css  js  c++  java
  • HDU2829

    题目大意:给定一个长度为n的序列,至多将序列分成m+1段,每段序列都有权值,权值为序列内两个数两两相乘之和。m<=n<=1000.

    分析:令w[i,j]表示区间[i,j]中两两乘积之和,f[i][j]表示前j个数分成i段的最小值。

    f[i][j]=f[i-1][k]+w[k+1,j]
    w[k+1,j]可以转换为w[1,j]-w[1,k]-sum[k]*(sum[j]-sum[k])
    其中sum[j]表示前j个数的前缀和。
    f[i][j]=f[i-1][k]+w[j]-w[k]-sum[k]*(sum[j]-sum[k])
    令y=f[i-1][k]+w[j]-w[k]+sum[k]^2,x=sum[k],b=sum[j],g=f[i][j],则有:

              y-bx=g

     此为直线方程,b为定值,要求g最小,即为直线的截距最小。平面上有若干点(x,y),这些点是由各个决策点产生的。而将直线从下往上平移,它接触到的第一个点即为最佳决策点。因为斜率b是上升的,所以,下一阶段的直线方程斜率更高,于是最佳决策点一定形成了下凸包序列。

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #define MAXN 1005
    #define LL long long int
    LL f[MAXN][MAXN],w[MAXN],sum[MAXN];
    #define FZ(i,p) (f[i-1][p]-w[p]+sum[p]*sum[p])
    int n,m,num[MAXN];
    int que[MAXN],head,tail;
    #define MAXZ (1LL<<22)
    bool turnup(int i,int p1,int p2,int p3) //p1>p2>p3 
    {
        LL y1=FZ(i,p1);
        LL x1=sum[p1];
        LL y2=FZ(i,p2);
        LL x2=sum[p2];
        LL y3=FZ(i,p3);
        LL x3=sum[p3];
        if((x2-x3)*(y1-y2)>(x1-x2)*(y2-y3))return 1;
            else return 0;
    }
    int main()
    {
        while(scanf("%d%d",&n,&m)&&(n||m))
        {
            memset(sum,0,sizeof sum);
            memset(w,0,sizeof w);
        m++;
        for(int i=1;i<=n;i++)
        {scanf("%d",&num[i]);
         sum[i]=sum[i-1]+num[i];
            w[i]=w[i-1]+sum[i-1]*num[i];
        }
        for(int i=1;i<=n;i++)
            f[1][i]=w[i];
        for(int i=2;i<=m;i++)
        {
            //f[i-1][i-1]=0;
            head=tail=1;
            que[tail++]=i-1;
            for(int j=i;j<=n;j++)
            {
                while(head<tail-1&&FZ(i,que[head+1])-FZ(i,que[head])<sum[j]*(sum[que[head+1]]-sum[que[head]]))head++;
                int k=que[head];
                f[i][j]=f[i-1][k]+w[j]-w[k]-sum[k]*(sum[j]-sum[k]);
                while(head<tail-1&&turnup(i,j,que[tail-1],que[tail-2])==0)
                    tail--;
                que[tail++]=j;
            }
        }
        /*
        for(int i=1;i<=m;i++)
        {for(int j=1;j<=n;j++)
                printf("%I64d ",f[i][j]);
            printf("
    ");
        }
        */
        printf("%I64d
    ",f[m][n]);
    }
    }

    本题也可以用平行四边形优化。

    f[i][j]=f[i-1][k]+w[k+1,j]

    w[k+1,j]很明显满足区间包含性质和平行四边形性质,所以f[i][j]也满足平行四边形性质,所以设s[i][j]表示f[i][j]的最佳决策点。s[i-1][j]<=s[i][j]<=s[i][j+1]。

    从常识角度来思考,也是比较好想的。

    如果数组元素不变,分段数增加,肯定分界点会往两边扩展,若分段数减少,则分界点会往中间靠拢,所以s[i][j]<=s[i+1][j]

    如果分段数不变,数的个数增加(右边补充进来),分界点应该往右微调;如果数的个数减少(从右边剔除),分界点应该往左微调,所以有s[i][j-1]<=s[i][j]

    所以s[i-1][j]<=s[i][j]<=s[i][j+1]

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #define MAXN 1005
     5 #define MAXM 1005
     6 #define LL long long int
     7 #define min(a,b) ((a)<(b)?(a):(b))
     8 LL f[MAXN][MAXM],sum[MAXN],w[MAXN],s[MAXN][MAXM];
     9 int num[MAXN],t,n,m;
    10 using namespace std;
    11 void pre()
    12 {
    13     for(int i=1;i<=n;i++)
    14         sum[i]=sum[i-1]+num[i];
    15     for(int i=1;i<=n;i++)
    16         w[i]=w[i-1]+sum[i-1]*num[i];
    17     }
    18 int main()
    19     {
    20         while(scanf("%d%d",&n,&m)&&(n||m))
    21         {
    22             m++;
    23             for(int i=1;i<=n;i++)
    24                 scanf("%d",&num[i]);
    25             memset(f,0x3f,sizeof f);
    26             pre();
    27             for(int i=1;i<=n;i++)
    28             {for(int j=i;j<=n;j++)
    29                     s[i][j]=i-1;
    30             }
    31             for(int i=1;i<=n;i++)
    32                 s[i][m+1]=i-1;
    33             for(int i=1;i<=n;i++)
    34                 f[i][i]=0;
    35             for(int j=1;j<=n;j++)
    36                 f[j][1]=w[j];
    37             
    38             for(int i=1;i<=n;i++)
    39                 for(int j=min(m,i);j>=1;j--)
    40                 {
    41                     for(int k=s[i][j+1];k>=s[i-1][j];k--)
    42                     {
    43                         LL temp=f[k][j-1]+w[i]-w[k]-sum[k]*(sum[i]-sum[k]);
    44                         if(f[i][j]>temp)
    45                         {f[i][j]=temp;
    46                             s[i][j]=k;
    47                         }
    48                     }
    49                 }
    50                 printf("%I64d
    ",f[n][m]);
    51             }
    52         }
    View Code
  • 相关阅读:
    python
    基于vue的npm发包
    div实现水平垂直居中
    element-ui中表格添加fixed定位列后 出现表格错位现象
    解决github经常无法访问的问题
    快排算法C语言实现
    ubuntu下qt运行时/usr/bin/ld: cannot find -lGL
    llinux装完qt 启动qtcreator报错
    LINUX权限-bash: ./startup.sh: Permission denied
    In short, don’t use a pointer to a string literal if you plan to alter the string.
  • 原文地址:https://www.cnblogs.com/hefenghhhh/p/4597517.html
Copyright © 2011-2022 走看看