zoukankan      html  css  js  c++  java
  • BZOJ 1044 木棍分割(二分答案 + DP优化)

    题目链接  木棍分割

    1044: [HAOI2008]木棍分割

    Time Limit: 10 Sec  Memory Limit: 162 MB
    Submit: 3830  Solved: 1453
    [Submit][Status][Discuss]

    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)

    Source

    第一个问题显然用二分解决,答案为len

    第二个问题考虑DP,f[i][j]表示前i根木棒切j刀之后长度最大的一段不超过len的方案数。

    则状态转移方程为  f[i][j] = sum(f[k][j - 1])

    其中k必须满足  s[i] - s[k] <= len

    这样做的话空间复杂度和时间复杂度都超过了限制,所以我们需要优化

    空间复杂度方面,因为f[i][j]是从f[i][j - 1]推导而来,所以f的第二维可以用滚动数组来代替。

    时间复杂度方面,我们发现大量的时间浪费在判断s[i] - s[k] <= len上,

    因为随着j的递增每次符合s[i] - s[k] <= len的k的最大值是不下降的,

    那么维护一个类似two-pointer的东西就可以了。

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define rep(i, a, b)	for (int i(a); i <= (b); ++i)
    #define dec(i, a, b)	for (int i(a); i >= (b); --i)
    
    typedef long long LL;
    
    const int N   = 50010;
    const int mod = 10007;
    
    
    int n, m;
    int a[N], s[N], f[N][2];
    int l, r, ans;
    int len, cnt;
    int k, now;
    
    inline bool check(int x){
    	int now = a[1], ret = 0;
    	rep(i, 2, n){
    		if (now + a[i] <= x){
    			now += a[i];
    		}
    
    		else{
    			now = a[i];
    			++ret;
    		}
    	}
    
    	return ret <= m;
    }       
    
    
    int main(){
    
    	scanf("%d%d", &n, &m);
    	rep(i, 1, n){
    		scanf("%d", a + i);
    		l = max(l, a[i]);
    		r += a[i];
    		s[i] = s[i - 1] + a[i];
    	}
    
    	l = 0, r = 1e8;
    
    	while (l + 1 < r){
    		int mid = (l + r) >> 1;
    		if (check(mid)) r = mid;
    		else l = mid + 1;
    	}
    
    	if (check(l)) len = l; else len = r;
    
    	rep(i, 0, m){
    		cnt = 0;
    		k = 1;
    		rep(j, 1, n){
    			if (i == 0){
    				if (s[j] <= len) f[j][now] = 1;
    				else f[j][now] = 0;
    			}
    
    			else{
    				while (k < j && s[j] - s[k] > len){
    					cnt -= f[k][now ^ 1];
    					cnt = (cnt + mod) % mod;
    					++k;
    				}
    				f[j][now] = cnt;
    			}
    
    			cnt = (cnt + f[j][now ^ 1]) % mod;
    		}
    
    		(ans += f[n][now]) %= mod;
    		now ^= 1;
    	}
    
    	printf("%d %d
    ", len, ans);
    	return 0;
    }
    
  • 相关阅读:
    angular读书笔记(三)
    angular读书笔记(二)
    angularjs读书笔记(一)
    angular和web前端的一些细节
    angular的service
    angular学习之directive
    最近学的twig
    最近学的grunt
    今天学的angularJS
    android即时通讯开发笔记(一)绪论~实现用户登录,自动登录,注销功能
  • 原文地址:https://www.cnblogs.com/cxhscst2/p/7261962.html
Copyright © 2011-2022 走看看