zoukankan      html  css  js  c++  java
  • [bzoj1044][HAOI2008][木棍分割] (二分+贪心+dp+队列优化)

    Description

      有n根木棍, 第i根木棍的长度为Li,n根木棍依次连结了一起, 总共有n-1个连接处. 现在允许你最多砍断m个连
    接处, 砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长度最小, 并且输出有多少种砍的方法使得总长
    度最大的一段长度最小. 并将结果mod 10007。。。

    Input

      输入文件第一行有2个数n,m.接下来n行每行一个正整数Li,表示第i根木棍的长度.n<=50000,0<=m<=min(n-1,10
    00),1<=Li<=1000.

    Output

      输出有2个数, 第一个数是总长度最大的一段的长度最小值, 第二个数是有多少种砍的方法使得满足条件.

    Sample Input

    3 2 
    1 
    1
    10

    Sample Output

    10 2

    HINT

    两种砍的方法: (1)(1)(10)和(1 1)(10)

    Solution

    from Joe Fan,一个写得很好的题解

    第一问是一个十分显然的二分,贪心Check(),很容易就能求出最小的最大长度 Len 。

    第二问求方案总数,使用 DP 求解。

      使用前缀和,令 Sum[i] 为前 i 根木棍的长度和。

      令 f[i][j] 为前 i 根木棍中切 j 刀,并且满足最长长度不超过 j 的方案数,那么:

        状态转移方程: f[i][j] = Σ f[k][j-1]   ((1 <= k <= i-1) &&  (Sum[i] - Sum[k] <= Len))  

      这样的空间复杂度为 O(nm) ,时间复杂度为 O(n^2 m) 。显然都超出了限制。

      下面我们考虑 DP 的优化。

      1) 对于空间的优化。

        这个比较显然,由于当前的 f[][j] 只与 f[][j-1] 有关,所以可以用滚动数组来实现。

        f[i][Now] 代替了 f[i][j] , f[i][Now^1] 代替了 f[i][j-1] 。为了方便,我们把 f[][Now^1] 叫做 f[][Last] 。

        这样空间复杂度为 O(n) 。满足空间限制。

      2) 对于时间的优化。

        考虑优化状态转移的过程。

        对于 f[i][Now] ,其实是 f[mink][Last]...f[i-1][Last] 这一段 f[k][Last] 的和,mink 是满足 Sum[i] - Sum[k] <= Len 的最小的 k ,那么,对于从 1 到 n 枚举的 i ,相对应的 mink 也一定是非递减的(因为 Sum[i] 是递增的)。我们记录下 f[1][Last]...f[i-1][Last] 的和 Sumf ,mink 初始设为 1,每次对于 i 将 mink 向后推移,推移的同时将被舍弃的 p 对应的 f[p][Last] 从 Sumf 中减去。那么 f[i][Now] 就是 Sumf 的值。

        这样时间复杂度为 O(nm) 。满足时间限制。

    //KaibaSeto 20170128
    #include <stdio.h>
    #include <memory.h>
    #define MaxN 50010
    #define MaxBuf 1<<22
    #define mo 10007
    #define RG register
    #define mid ((x>>1)+(y>>1)+(x&y&1))
    char B[MaxBuf],*p=B;
    template<class Type>inline void Rin(RG Type &x){
        x=0;
        while(*p<'0'||*p>'9')p++;
        while(*p>='0'&&*p<='9')
            x=(x<<1)+(x<<3)+*p++-'0';
    }
    int n,m,l[MaxN],s[MaxN],ans,cnt,f[2][MaxN];
    inline bool Jud(RG int lim){
        RG int tot=0,block_num=0;
        for(RG int i=1;i<=n;i++){
            if(l[i]>lim)return false;
            tot+=l[i];
            if(tot>lim){
                block_num++; tot=l[i];
            }
            if(block_num>m)return false;
        }
        return true;
    }
    inline void bin_search(){
        RG int x=1,y=s[n];
        while(x<=y)
            Jud(mid)?(ans=mid,y=mid-1):x=mid+1;
        printf("%d ",ans);
    }
    inline void approach(){
        RG int c=0;
        for(RG int i=0;i<=m;i++){
            c^=1;
            RG int tot=0,k=1;
            for(RG int j=1;j<=n;j++){
                if(!i)f[c][j]=(s[j]<=ans);
                else{
                    while(k<j&&s[j]-s[k]>ans){
                        tot-=f[c^1][k];
                        (tot+=mo)%=mo;
                        ++k;
                    }
                    f[c][j]=tot;
                }
                (tot+=f[c^1][j])%=mo;
            }
            (cnt+=f[c][n])%=mo;
        }
        printf("%d
    ",cnt);
    }
    #define FO(x) {freopen(#x".in","r",stdin);}
    int main(){
        FO(bzoj1044);
        fread(p,1,MaxBuf,stdin);
        Rin(n),Rin(m);
        for(RG int i=1;i<=n;i++){
            Rin(l[i]);
            s[i]=s[i-1]+l[i];
        }
        bin_search();
        approach();
        return 0;
    }
  • 相关阅读:
    React 之使用 fetch
    react-native 环境搭建
    create-react-app 配置 less
    React新的前端思维方式
    字体图标 —— IconMoon
    你不知道的javascript 之 >>
    前端的自我修养
    jquery 学习
    html的meta总结
    git基本操作 nginx基本操作
  • 原文地址:https://www.cnblogs.com/keshuqi/p/6356958.html
Copyright © 2011-2022 走看看