zoukankan      html  css  js  c++  java
  • 【动态规划】bzoj1044: [HAOI2008]木棍分割

    需要滚动优化或者short int卡空间

    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)


    题目分析

    昨天做到这道题的稍微强化版:允许空集存在。我那题想法是,强制没有空集,最后再用组合数统计答案。状态$f[i][j]$表示前$i$个数分为$j$个非空集合的方案数,$t[i]$表示最小能够与$i$合并的位置。于是$f[i][j]=sum_{x=t[i]}^{i}{f[i][j-1]}$。这里有一个$sum_{i}{f[i][j]}$的形式,自然用前缀和优化。

    组合数溢出调了好久……

     1 #include<bits/stdc++.h>
     2 const int maxn = 1003;
     3 const int MO = 998244353;
     4 
     5 int n,m,mxBound,mnBound,ans;
     6 int a[maxn],s[maxn],t[maxn],g[maxn][maxn];
     7 int f[maxn][maxn];
     8 int C[maxn][maxn];
     9 
    10 int read()
    11 {
    12     char ch = getchar();
    13     int num = 0;
    14     bool fl = 0;
    15     for (; !isdigit(ch); ch = getchar())
    16         if (ch=='-') fl = 1;
    17     for (; isdigit(ch); ch = getchar())
    18         num = (num<<1)+(num<<3)+ch-48;
    19     if (fl) num = -num;
    20     return num;
    21 }
    22 bool check(int x)
    23 {
    24     int cnt = 1, sum = 0;
    25     for (int i=1; i<=n; i++)
    26         if (sum+a[i] > x) sum = a[i], cnt++;
    27         else sum += a[i];
    28     return cnt <= m;
    29 }
    30 int qmi(int a, int b)
    31 {
    32     int ret = 1;
    33     while (b)
    34     {
    35         if (b&1) ret = 1ll*ret*a%MO;
    36         a = 1ll*a*a%MO, b >>= 1;
    37     }
    38     return ret;
    39 }
    40 int main()
    41 {
    42 //    freopen("ex_t2.in","r",stdin);
    43     n = read(), m = read(), C[0][0] = 1;
    44     for (int i=1; i<=m; i++)
    45     {
    46         C[i][0] = 1;
    47         for (int j=1; j<=i; j++)
    48             C[i][j] = (C[i-1][j]+C[i-1][j-1])%MO;
    49     }
    50     for (int i=1; i<=n; i++)
    51         a[i] = read(), mxBound += a[i], mnBound = mnBound < a[i]?a[i]:mnBound, s[i] = s[i-1]+a[i];
    52     int l = mnBound, r = mxBound, head = 1, tot = 0;
    53     for (int mid=(l+r)>>1; l<=r; mid=(l+r)>>1)
    54         if (check(mid)) ans = mid, r = mid-1;
    55         else l = mid+1;
    56     printf("%d
    ",ans);
    57     for (int i=1; i<=n; i++)
    58     {
    59         tot += a[i];
    60         while (tot > ans) tot -= a[head++];
    61         t[i] = head;
    62     }
    63     g[0][0] = f[0][0] = 1, ans = 0;
    64     for (int i=1; i<=n; i++)
    65     {
    66         g[i][0] = 1;
    67         for (int j=1; j<=m; j++)
    68         {
    69             int delta = g[i-1][j-1];
    70             if (t[i]>1) delta -= g[t[i]-2][j-1];
    71             (f[i][j] += 1ll*delta+MO) %= MO;
    72             (g[i][j] = 1ll*g[i-1][j]+1ll*f[i][j]) %= MO;
    73         }
    74     }
    75     for (int i=1; i<=m; i++)
    76         ans = (ans+1ll*C[m][i]*f[n][i])%MO;
    77     printf("%d
    ",ans);
    78     return 0;
    79 }

    但是强化版空间512M,这题却只有162M。

    首当其冲想到滚动数组优化,不过由于在这题里滚动数组需要把$j$放在枚举的外层,因此在一些奇怪的原因影响下效率会变得非常低。

    注意到本题的模数非常小(不知道是不是出题人的善意),于是可以把$f[i][j]$开成short int省去一半空间。

    再注意到其实同时开$f[i][j]$与$g[i][j]$是没有必要的,我们完全可以把$f[i][j]$表示成$g[i][j]$的前缀和形式,从而又省去一半空间。

     1 #include<bits/stdc++.h>
     2 const int maxn = 50003;
     3 const int maxm = 1003;
     4 const int MO = 10007;
     5 
     6 int n,m,mxBound,mnBound,ans;
     7 int a[maxn],s[maxn],t[maxn];
     8 short int g[maxn][maxm];
     9 
    10 int read()
    11 {
    12     char ch = getchar();
    13     int num = 0;
    14     bool fl = 0;
    15     for (; !isdigit(ch); ch = getchar())
    16         if (ch=='-') fl = 1;
    17     for (; isdigit(ch); ch = getchar())
    18         num = (num<<1)+(num<<3)+ch-48;
    19     if (fl) num = -num;
    20     return num;
    21 }
    22 bool check(int x)
    23 {
    24     int cnt = 1, sum = 0;
    25     for (int i=1; i<=n; i++)
    26         if (sum+a[i] > x) sum = a[i], cnt++;
    27         else sum += a[i];
    28     return cnt <= m;
    29 }
    30 int main()
    31 {
    32 //     freopen("1044.in","r",stdin);
    33 //     freopen("1044.out","w",stdout);
    34     n = read(), m = read()+1;
    35     for (int i=1; i<=n; i++)
    36         a[i] = read(), mxBound += a[i], mnBound = mnBound < a[i]?a[i]:mnBound, s[i] = s[i-1]+a[i];
    37     int l = mnBound, r = mxBound, head = 1, tot = 0;
    38     for (int mid=(l+r)>>1; l<=r; mid=(l+r)>>1)
    39         if (check(mid)) ans = mid, r = mid-1;
    40         else l = mid+1;
    41     printf("%d ",ans);
    42     for (int i=1; i<=n; i++)
    43     {
    44         tot += a[i];
    45         while (tot > ans) tot -= a[head++];
    46         t[i] = head;
    47     }
    48     register int i,j;
    49     ans = 0;
    50     for (i=0; i<=n; i++) g[i][0] = 1;
    51     for (int i=1; i<=n; i++)
    52     {
    53         for (int j=1; j<=m; j++)
    54         {
    55             int delta = g[i-1][j-1];
    56             if (t[i]>1) delta -= g[t[i]-2][j-1];
    57             (g[i][j] = g[i-1][j]+delta) %= MO;
    58         }
    59     }
    60     for (i=1; i<=m; i++)
    61         ans = (ans+g[n][i]-g[n-1][i]+MO)%MO;
    62     printf("%d
    ",ans);
    63     return 0;
    64 }

    END

  • 相关阅读:
    Add Binary
    Java笔记之String
    Java笔记之数组
    Merge Two Sorted Lists
    Remove Nth Node From End of List
    Longest Common Prefix
    Roman to Integer
    Palindrome Number
    Reverse Integer
    _cdel stdcall
  • 原文地址:https://www.cnblogs.com/antiquality/p/9418259.html
Copyright © 2011-2022 走看看