CF1066D Boxes Packing 题解
题意简化:
有个数,第i个数为,你需要选出至多个子序列,使得每一个子序列的和都<=k,并且这些子序列在原序列里面必须连续且靠右。问能选出多少个数。
1.暴力
我们可以枚举一个数(1<=<=),表示前i个数不选,选第到第个数,再利用如下的代码进行判断。如果返回true,那么输出。时间复杂度。
bool check(int i)
{
//从第i+1个数选到第N个数
//tmp表示当前选的数的和
//sum表示已经选了多少个子序列
int tmp=0,sum=0;
for(int j=i+1;j<=n;j++)
{
if(tmp+a[j]>=k)//如果当前子序列的和>=k
{
sum++;//进入下一个子序列
tmp=a[j];//把这个子序列的初始和记为a[j]
}
else tmp+=a[j];//否则,就可以选第j个数,把当前子序列的和加上a[j]
}
if(sum>m) return 0;//如果子序列的个数>m,方案不可行,返回false
return 1;//否则返回true
}
当然,一看数据范围:1<=<=,就知道肯定要炸。所以我们需要一种更快的方法。
2.线性
这道题正着做时间复杂度要爆炸。
我们数学老师说过一句话:“正若难,则反。”
所以我们可以反着做。
从第个数开始倒推(设当前的数为),把第个数加入当前这个子序列,如果第个数加入后子序列>=个,那么前个数就是要抛弃的数。当然如果数字选完了也要退出。时间复杂度为,足够通过本题。
代码如下:
#include<bits/stdc++.h>
using namespace std;
int n,m,k,a[200000+10],sum,t;
int box[200000+10];
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=n;i++) sum+=a[i];
t=n;//将t初始化为$N$
while(m!=0&&t!=0)//当子序列还没有用尽或者是数字还没有选完的时候
{
box[m]+=a[t];//将第M个子序列的和加上a[t]
if(box[m]>k)//如果第M个子序列的和>k,切换到下一个子序列
{
m--;//下一个子序列
box[m]=a[t];//初始化新子序列的和=a[t]
}
t--;//指向下一个数
}
if(m==0) t++;//如果子序列选完了,那么第t个数就不能加入子序列里面,需要t++,因为第t+1个数是可以放进子序列
printf("%d\n",n-t);//输出选择的个数
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具