c语言中的切木棍有几种方法,木棍分割[HAOI2008]

本文介绍了如何使用C语言通过二分法解决木棍分割问题,涉及二分答案的策略和注意事项,以及如何优化空间和时间复杂度。通过检查不同分割值的可行性,最终找到满足条件的最小解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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

题解

人生第一道二分答案成就感满满!二分答案,是指在最小值和最大值之间二分枚举答案,逐渐把答案缩小到一个值得出最优解。二分答案的题目设问常常是最大值最小、最小值最大,解的可能性应该是单调的(即某值不可行则答案只能比它大或只能比它小),需要能简单地验证答案正确与否,要注意的就是防止死循环(不过这道题好像没有这个问题)。对于木棍分割,如果这个值可行,最优解可能比它更小,以它为上界看一看有没有更小的解;如果这个值不可行,只能舍弃它和比它更小的解,以它+1为下界搜索答案。

int check(long long x)

{

temp=he=0;

for(int i=1;i<=n;i++)

{

if(he+li[i]<=x)

he+=li[i];

else

{

he=li[i];

temp++;

if(temp>m)

return 0;

}

}

if(temp>m) return 0;

return 1;

}

le=yd,jg=zd;(最小解是单段木块长度的最大值,最大解是所有木块长度之和)

while(le

{

mid=(le+jg)>>1;

if(check(mid)) jg=mid;

else le=mid+1;

}

dp部分优化空间复杂度用滚动数组,优化时间复杂度用类似单调队列的思路,和Watching firework is fun大同小异,最后结果为f[i][n]之和。f[i][j]表示j段木块切i次的方案,f[i][j]为所有满足sum[i]-sum[k]<=jg的f[k][j-1]之和(1<=k

UPD:关于这个二分卡死的问题后来慢慢弄清了是怎么回事。并不是+1或不+1就容易被卡死,而是你要保证每次l和r总有一个是变化的。比如说mid=l+r>>1,那mid可能和l相等,你就不能if(……)l=mid,如果必须这样就得mid=(l+r+1)>>1。OI中的很多东西开始不明白,用了很长时间忽然就搞懂了,只有看清了内部的原理才是真正的通透啊。

#include

#include

#include

using namespace std;

const int sj=;

int li[sj],mod=,yd;

int la,no,head,n,m,mid,temp,tail;

long long s[sj],f[][sj],zd,jg,le,he,fas;

int dbj(int x,int y)

{

return x>y?x:y;

}

int xbj(int x,int y)

{

return x

}

void init()

{

scanf("%d%d",&n,&m);

for(int i=;i<=n;i++)

{

scanf("%d",&li[i]);

s[i]=s[i-]+li[i];

zd+=li[i];

yd=dbj(li[i],yd);

}

}

void dp()

{

for(int i=;i<=n;i++)

{

if(s[i]<=jg)

f[][i]=;

if(s[i]>jg)

break;

}

fas=f[][n];

for(int j=;j<=m;j++)

{

head=;

tail=;

he=;

la=(j+)%;

no=j%;

for(int i=;i<=n;i++)

{

if(i

for(int k=tail+;k

he+=f[la][k];

tail=i-;

while(s[i]-s[head]>jg)

{

he-=f[la][head];

head++;

}

f[no][i]=he;

f[no][i]%=mod;

}

fas+=f[no][n];

fas%=mod;

}

}

int check(long long x)

{

temp=he=;

for(int i=;i<=n;i++)

{

if(he+li[i]<=x)

he+=li[i];

else

{

he=li[i];

temp++;

if(temp>m)

return ;

}

}

if(temp>m) return ;

return ;

}

int main()

{

init();

le=yd,jg=zd;

while(le

{

mid=(le+jg)>>;

if(check(mid)) jg=mid;

else le=mid+;

}

printf("%lld\n",jg);

dp();

printf("%lld",fas);

//while(1);

return ;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值