zoukankan      html  css  js  c++  java
  • 【BZOJ】【1044】【HAOI2008】木棍分割

    二分/DP


      真是一道好题!

      第一问很简单的二分……

      第二问一开始我想成贪心了,其实应该是DP的= =

      然后没有注意……又MLE又TLE的……这题要对DP进行时空两方面的优化!!

      题解:(by JoeFan)

    使用前缀和,令 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) 。满足时间限制。

     1 /**************************************************************
     2     Problem: 1044
     3     User: Tunix
     4     Language: C++
     5     Result: Accepted
     6     Time:4152 ms
     7     Memory:4396 kb
     8 ****************************************************************/
     9  
    10 //BZOJ 1044
    11 #include<vector>
    12 #include<cstdio>
    13 #include<cstring>
    14 #include<cstdlib>
    15 #include<iostream>
    16 #include<algorithm>
    17 #define rep(i,n) for(int i=0;i<n;++i)
    18 #define F(i,j,n) for(int i=j;i<=n;++i)
    19 #define D(i,j,n) for(int i=j;i>=n;--i)
    20 #define pb push_back
    21 using namespace std;
    22 inline int getint(){
    23     int v=0,sign=1; char ch=getchar();
    24     while(ch<'0'||ch>'9'){ if (ch=='-') sign=-1; ch=getchar();}
    25     while(ch>='0'&&ch<='9'){ v=v*10+ch-'0'; ch=getchar();}
    26     return v*sign;
    27 }
    28 const int N=1e5+10,INF=~0u>>2,P=10007;
    29 typedef long long LL;
    30 /******************tamplate*********************/
    31 int n,m,ans,a[N],f[N][2],pos[N],sumf[N][2];
    32 LL s[N];
    33 bool check(int len){
    34     int cnt=0,sum=0;
    35     F(i,1,n){
    36         if (a[i]>len) return 0;
    37         if (sum+a[i]>len){
    38             cnt++; sum=a[i];
    39         }else sum+=a[i];
    40     }
    41     return cnt<=m;
    42 }
    43 int main(){
    44 #ifndef ONLINE_JUDGE
    45     freopen("1044.in","r",stdin);
    46     freopen("1044.out","w",stdout);
    47 #endif
    48     n=getint(); m=getint();
    49     F(i,1,n) {a[i]=getint();s[i]=s[i-1]+a[i];}
    50      
    51     int l=0,r=s[n],mid;
    52     while(l<=r){
    53         mid=l+r>>1;
    54         if (check(mid)) ans=mid,r=mid-1;
    55         else l=mid+1;
    56     }
    57     printf("%d ",ans);
    58     F(i,0,n) sumf[i][0]=1;
    59     int way=0;
    60     F(j,1,m+1){
    61         int now=j&1; 
    62         sumf[0][now]=0;
    63         F(i,1,n){
    64             if (!pos[i]) 
    65                 pos[i]=pos[i-1]; while(s[i]-s[pos[i]]>ans) pos[i]++;
    66             f[i][now]=(sumf[i-1][now^1]-sumf[pos[i]-1][now^1]+P)%P;
    67             sumf[i][now]=(sumf[i-1][now]+f[i][now])%P;
    68         }
    69         way=(way+f[n][now])%P;
    70     }
    71     printf("%d
    ",way);
    72     return 0;
    73 }
    View Code

    1044: [HAOI2008]木棍分割

    Time Limit: 10 Sec  Memory Limit: 162 MB
    Submit: 2008  Solved: 725
    [Submit][Status][Discuss]

    Description

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

    Input

    输入文件第一行有2个数n,m. 接下来n行每行一个正整数Li,表示第i根木棍的长度.

    Output

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

    Sample Input

    3 2
    1
    1
    10

    Sample Output

    10 2

    HINT

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

    数据范围 

       n<=50000, 0<=m<=min(n-1,1000).

       1<=Li<=1000.

    Source

    [Submit][Status][Discuss]
  • 相关阅读:
    Balance的数学思想构造辅助函数
    1663. Smallest String With A Given Numeric Value (M)
    1680. Concatenation of Consecutive Binary Numbers (M)
    1631. Path With Minimum Effort (M)
    1437. Check If All 1's Are at Least Length K Places Away (E)
    1329. Sort the Matrix Diagonally (M)
    1657. Determine if Two Strings Are Close (M)
    1673. Find the Most Competitive Subsequence (M)
    1641. Count Sorted Vowel Strings (M)
    1679. Max Number of K-Sum Pairs (M)
  • 原文地址:https://www.cnblogs.com/Tunix/p/4429655.html
Copyright © 2011-2022 走看看