zoukankan      html  css  js  c++  java
  • BZOJ 3675: 序列分割 (斜率优化dp)

    Description

    小H最近迷上了一个分隔序列的游戏。在这个游戏里,小H需要将一个长度为n的非负整数序列分割成k+1个非空的子序列。为了得到k+1个子序列,小H需要重复k次以下的步骤:
    1.小H首先选择一个长度超过1的序列(一开始小H只有一个长度为n的序列——也就是一开始得到的整个序列);
    2.选择一个位置,并通过这个位置将这个序列分割成连续的两个非空的新序列。
     
    每次进行上述步骤之后,小H将会得到一定的分数。这个分数为两个新序列中元素和的乘积。小H希望选择一种最佳的分割方式,使得k轮之后,小H的总得分最大。
     

    Input

    输入第一行包含两个整数n,k(k+1≤n)。

    第二行包含n个非负整数a1,a2,...,an(0≤ai≤10^4),表示一开始小H得到的序列。

    Output

    输出第一行包含一个整数,为小H可以得到的最大分数。

    Sample Input

    7 3
    4 1 3 4 0 2 3

    Sample Output

    108 
    题意:给出一个包含n个非负整数的序列,要求将其分割成k+1个序列,每次分割可以获得一定的分数,分数=序列分割位置左侧的数之和×序列分割位置右侧的数之和。要求最大分数是多少。
    思路:值得注意的是最后得到的分数与分割的先后顺序无关 所以我们可以大胆的进行划分dp[i][j]表示前i个数分割j次得到的最大分数
    dp[i][j]=max(dp[k][j-1]+sum[k]*(sum[i]-sum[k])) 这里略过斜率优化的证明 我们还可以发现 可以用滚动数组降低空间复杂度
    细节:斜率分母可能为0 所以要特判一下
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<iostream>
    #include<string>
    #include<vector>
    #include<stack>
    #include<bitset>
    #include<cstdlib>
    #include<cmath>
    #include<set>
    #include<list>
    #include<deque>
    #include<map>
    #include<queue>
    #define ll long long int
    using namespace std;
    inline ll gcd(ll a,ll b){return b?gcd(b,a%b):a;}
    inline ll lcm(ll a,ll b){return a/gcd(a,b)*b;}
    int moth[13]={0,31,28,31,30,31,30,31,31,30,31,30,31};
    int dir[4][2]={1,0 ,0,1 ,-1,0 ,0,-1};
    int dirs[8][2]={1,0 ,0,1 ,-1,0 ,0,-1, -1,-1 ,-1,1 ,1,-1 ,1,1};
    const int inf=0x3f3f3f3f;
    const ll mod=1e9+7;
    ll n,k;
    ll a[100007];
    ll dp[100007][2];
    ll sum[100007];
    ll q[100007];
    double slope(ll j,ll k,ll zu){
        return sum[k]-sum[j]==0?0:((dp[j][zu]-sum[j]*sum[j]-dp[k][zu]+sum[k]*sum[k])
        /(sum[k]-sum[j]));
    }
    int main(){
        ios::sync_with_stdio(false);
        while(cin>>n>>k){
            for(int i=1;i<=n;i++)
                cin>>a[i],sum[i]=sum[i-1]+a[i];
            int l,r;
            for(int j=1;j<=k;j++){ //分割次数 
                l=r=1;
                for(int i=1;i<=n;i++){ //人数 
                    while(l<r&&slope(q[l],q[l+1],(j-1)%2)<sum[i]) l++;
                    dp[i][j%2]=dp[q[l]][(j-1)%2]+sum[q[l]]*(sum[i]-sum[q[l]]);
                    while(l<r&&slope(q[r-1],q[r],(j-1)%2)>slope(q[r],i,(j-1)%2)) r--;
                    q[++r]=i;    
                }
            }
            cout<<dp[n][k%2]<<endl;
        }
        return 0;
    }

     

  • 相关阅读:
    银行业务调度系统
    交通灯管理系统
    Java高新技术
    Java反射机制
    java的集合框架
    正则表达式
    IPD CBB
    TCP的可靠传输(依赖流量控制、拥塞控制、连续ARQ)
    等价类划分
    Pycharm常用配置汇总
  • 原文地址:https://www.cnblogs.com/wmj6/p/10797847.html
Copyright © 2011-2022 走看看