【bzoj1044】木棍分割

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

Solution

第一问我们使用二分查找查询每个区间的长度上线是多少

对于第二问,我们使用动态规划

f[i][j]=simga(f[k][j-1])(s[i]-s[k]<=lim)

显然的我们可以使用前缀和来解决问题

然后对于每个i,k最小能取到多少是确定的,我们预处理一下,

然后加个滚动数组就一遍a了,开心!

 

 

#include<stdio.h>
#include<stdlib.h>
#include<iostream>
#include<string>
#include<string.h>
#include<algorithm>
#include<math.h>
#include<queue>
#include<map>
#include<vector>
#include<set>
#define il inline
#define re register
#define mod 10007
using namespace std;
const int N=51111;
int n,m,a[N],l=0,r=0,mid,s[N],lim,p[N],f[2][N],lst,now,ans;
il bool chk(int lim){
    int cnt=0,tot=1;
    for(int i=1;i<=n;i++){
        if(cnt+a[i]<=lim){
            cnt+=a[i];
        }
        else{
            cnt=a[i];tot++;
        }
    }
    return tot<=m;
}
int main(){
    scanf("%d%d",&n,&m);m++;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        r+=a[i];l=max(l,a[i]);
    }
     
    while(l<r){
        mid=(l+r)/2;
        if(chk(mid)) r=mid;
        else l=mid+1;
    }
    lim=r;
    for(int i=1;i<=n;i++) s[i]=s[i-1]+a[i];
    for(int i=1;i<=n;i++){
        l=1;r=n;
        while(l<r){
            mid=(l+r)/2;
            if(s[i]-s[mid-1]<=lim) r=mid;
            else l=mid+1; 
        }
        p[i]=r; 
    }
    lst=0;now=1;
    for(int i=1;i<=n;i++) 
        if(s[i]<=lim) f[lst][i]=f[lst][i-1]+1;
        else f[lst][i]=f[lst][i-1];
    ans=(f[0][n]-f[0][n-1])%mod;
    for(int j=2;j<=m;j++){
        memset(f[now],false,sizeof(f[now]));
        for(int i=j,x;i<=n;i++){
            x=(f[lst][i-1]-f[lst][max(p[i]-2,0)])%mod;
            f[now][i]=(f[now][i-1]+x)%mod;
        }
        ans=(ans+f[now][n]-f[now][n-1]+mod)%mod;
        swap(now,lst);
    }
    cout<<lim<<" "<<ans;
    return 0;
}

 

转载于:https://www.cnblogs.com/ExiledPoet/p/6091194.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值