B4093 [CSP-X2021 山东] 发送快递

news/2025/11/12 20:21:17/文章来源:https://www.cnblogs.com/qwqSW/p/19210199

题目传送门

我的博客 - 欢迎光临!

本题弱化版(?):P3052,P10483

二者互为双倍经验。


首先对于一个强制捆绑组里的物品,我们直接使用并查集,将一个并查集里的物品合成为一个大物品就好了。那它就变成了 \(s=0\) 时的情况。

接下来均考虑 \(s=0\) 的情况。

先说说暴力做法吧,考场上打一打还是很有用的。

暴力做法 \(O(2^nn^2)\)

\(s=0\) 的情况下,这道题就是数据加强后的这俩题。详解可以看这里。

转述一下那个题的思路。设 \(dp_{i,S}\) 表示当前已经开到了第 \(i\) 班快递,并且已经邮出去的书的集合为 \(S\) 的情况下,最后一班快递的总重量最小是多少。

刷表法转移这个似乎更快一点。我们就考虑当前这个状态能转移到哪去。

我们枚举当前还没进快递的书包 \(k\),如果下一个进快递的是它,先给出结论:只需要两种情况,一种是新开一个快递让它装进去,一种是让它进最后一班快递。

为什么不考虑它进入先前快递的情况呢?因为我们发现,它进的如果是靠前一点的快递的话,那当这班快递还是最后一班快递的时候应该考虑过这个情况了。

我们细想一下,它在这班快递时可能有多本书和它一班,它和其他书的所有可能组合,都应该在这个靠前一点的快递还是最后一班快递时考虑过了。

剩下的就好懂多了。直接上暴力代码。

B4093暴力
#include<bits/stdc++.h>
using namespace std;const int N=24;
const int inf=0x3f3f3f3f;
int n,m,a[N],fa[N],dp[2][(1<<(N-1))+3],b[N],cnt,to[N];
string s;inline int FIND(int x){return (x==fa[x]?x:fa[x]=FIND(fa[x]));
}inline void MERGE(int x,int y){int fx=FIND(x),fy=FIND(y);if(fx==fy) return ;fa[fy]=fx;
}signed main(){scanf("%d%d",&n,&m);for(int i=1;i<=n;i++){scanf("%d",&a[i]);fa[i]=i;}int ss;scanf("%d",&ss);for(int i=0;i<=ss;i++){int x,lst=-1,num=0;//用了一个很猎奇的方法处理不定个数字:读字符串。。。 getline(cin,s);s=s+' ';int len=s.size();for(int j=0;j<len;j++){if(s[j]==' '){x=num;if(lst!=-1){//将上一个与它合并,这样一整个该行输入都会被合并起来 MERGE(x,lst);}lst=x;num=0;}else{num=num*10+s[j]-'0';}}}//b数组为合并后的数组,这里是在处理b for(int i=1;i<=n;i++){int fi=FIND(i);if(!to[fa[i]]){to[fa[i]]=++cnt;}b[to[fa[i]]]+=a[i];}n=cnt;for(int i=0;i<n;i++){a[i]=b[i+1];}for(int i=0;i<=n;i++){for(int S=0;S<(1<<n);S++){dp[(i&1)][S]=inf;}}//由于空间不够,所以dp数组用滚动数组优化空间 //初始化,第一班快递里什么书都不装的话,那总重显然是0 dp[1][0]=0;for(int i=0;i<=n;i++){for(int S=0;S<(1<<n);S++){if(dp[(i&1)][S]!=inf){//只有当前状态合法才能转移 for(int k=0;k<n;k++){//枚举当前没进快递的书包 if(S&(1<<k)) continue;//新开一班快递 dp[((i+1)&1)][(S|(1<<k))]=min(dp[((i+1)&1)][(S|(1<<k))],a[k]);//进最后一班快递 if(dp[(i&1)][S]+a[k]<=m){dp[(i&1)][(S|(1<<k))]=min(dp[(i&1)][(S|(1<<k))],dp[(i&1)][S]+a[k]);}}}}//如果当前用i班快递就能送完所有书的话,那答案就是i了 if(dp[(i&1)][(1<<n)-1]!=inf){printf("%d\n",i);break;}}return 0;
}

正解

100 分做法在此基础上,就是一句话的事情:你要把当前某个即将加入快递序列的书,放进当前最空的快递序列里。如果实在装不下,就新开一班快递。

那有的人就要问了:假设当前有一个 \(x\)\(x\) 可以装进 \(u,v\) 两班快递里(\(v\) 的剩余容量比 \(u\) 大),并且有一个 \(y\) 只能装进 \(v\) 里(并且 \(x,y\) 由于容量不够不能同时装进 \(v\) 里),那万一让 \(y\) 装进 \(v\)\(x\) 装进 \(u\) 的情况更优怎么办?

可是我们接着思考一下这个情况:当我们先考虑 \(y\) 而非 \(x\) 时,\(y\) 装进去的就是最空的那班快递序列 \(v\);然后再考虑 \(x\) 的话,就会考虑到 \(x\) 装进 \(u\) 的情况了(如果 \(u\) 是当前最空的话)。

或者我这么说,按我们这个逻辑考虑的话,对于有 \(x\)\(y\) 的情况 \(S\),让 \(x\)\(v\) 更优;有 \(y\)\(x\) 的情况 \(T\),也是让 \(y\)\(v\) 最优。

而当我们转移有 \(x\)\(y\) 的状态时,我们会考虑 \(S\) 状态加入 \(y\) 的转移,此时明显只能新开一班快递;但是如果是从 \(T\) 状态里加入 \(x\) 的话,\(x\) 可以进入 \(v\) 快递,就不需要再开一班快递了。所以显然是从 \(T\) 状态转移优。

所以我们这样相当于是考虑了这种情况、也考虑了一个不是很优的情况,但是最终转移肯定是从最优状态转移而来,所以它是正确的。

所以我们对于每个 \(S\) 维护它要开多少班快递,以及最空的那班快递的最小容量。

我们可以填表法转移,每次枚举最后一个进快递的书 \(i\) 并转移状态 \(S\) 即可。

然后我们就能以 \(O(n2^n)\) 的时间复杂度通过本题。考虑到极限情况下要跑 \(2 \times 10^8\),所以千万不要把常数写大。

代码:

B4093正解
#include<iostream>
#include<cstdio>
using namespace std;const int N=25;
const int inf=2147483647;
int n,m,a[N],fa[N],to[N],b[N],cnt;
string s;
struct sw{int cnt,val;
}dp[(1<<(N-1))+5]; 
//dp[S]:当前送出去的快递集合是S,最小要送多少班快递,并且每班快递里最空的一班快递的容量 
bool operator <(const sw SW,const sw WS){//对于一个状态来说,首先要让开的快递班数尽可能小,其次要让最小容量最小 return (SW.cnt==WS.cnt?SW.val<WS.val:SW.cnt<WS.cnt);
}inline int FIND(int x){return (x==fa[x]?x:fa[x]=FIND(fa[x]));
}inline void MERGE(int x,int y){int fx=FIND(x),fy=FIND(y);if(fx==fy) return ;fa[fy]=fx;
}signed main(){ios::sync_with_stdio(false);cin.tie(0),cout.tie(0);cin>>n>>m;for(int i=1;i<=n;i++){cin>>a[i];fa[i]=i;}int ss;cin>>ss;for(int i=0;i<=ss;i++){int x,lst=-1;cin>>lst;if(cin.get()=='\n') continue;while(cin>>x){MERGE(x,lst);if(cin.get()=='\n') break;}}//b数组为合并后的数组,这里是在处理b for(int i=1;i<=n;i++){int fi=FIND(i);if(!to[fa[i]]){to[fa[i]]=++cnt;}b[to[fa[i]]]+=a[i];}n=cnt;for(int i=0;i<n;i++){a[i]=b[i+1];}//预处理部分同上 for(int S=0;S<(1<<n);S++){dp[S]={N,inf};}for(int i=0;i<n;i++){dp[(1<<i)]={1,a[i]};}for(int S=1;S<(1<<n);S++){if((S&(S-1))==0) continue;//1后面很多0的情况,这样的情况是初始化了的,所以不参与转移//刚才为什么要提常数写大了的事呢。。。因为这里的mn只需要在第一层循环更新S状态,在第二层更新的话很可能T sw mn={N,inf};for(int i=0;i<n;i++){if((S&(1<<i))){sw s;if(a[i]+dp[S^(1<<i)].val>m){//新开一班快递 s={dp[S^(1<<i)].cnt+1,a[i]};}else{//挤进当前的快递 s={dp[S^(1<<i)].cnt,dp[S^(1<<i)].val+a[i]};}if(s<mn){mn=s;}}}dp[S]=mn;}int ans=dp[(1<<n)-1].cnt;cout<<ans;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/963735.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

从零上手 Rokid JSAR:打造专属 AR 桌面交互式 3D魔方,开启空间创建之旅

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

微软2025年11月补丁星期二修复1个零日漏洞和63个安全漏洞

微软2025年11月补丁星期二发布了安全更新,修复了63个安全漏洞,其中包括1个被积极利用的零日漏洞。本次更新包含4个严重级别漏洞,涉及权限提升、远程代码执行等多个安全领域。微软2025年11月补丁星期二修复1个零日漏…

CF468C Hack it!

已严肃完成今日构造大学习。 首先扔出来一个 \([1, 10^{18} - 1]\) 区间,你会发现这个区间每往右平移一个单位那么 \(f\) 的和就会加 \(1\),当然得在 \(2 \times 10^{18} - 1\) 之内,这也是我们选择这个界的原因。 …

深入解析:FT62FC3X 8位MCU单片机选型表,详细解析FT62FC31A/32A/33A/35A/3FA

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

FFmpeg for Android 图传Web

FFmpeg for Android & 图传Web 之前在项目研发中有个需求, 需要对接RTSP摄像头, 并且需要将其转封装格式到H5能够播放的格式, 并且需要在纯安卓APP中实现, 并且需要低延迟. 找了一圈都没有合适的且现成的方案, 这个…

语法记录

\(\texttt{titile}\) 计概与数算行末不要空格!!! 字符串与字符数组读入 cin >> str //字符与字符串,不含空格、制表符与换行 scanf("%c", &ch) //读单个字符 ch scanf(" %c", &…

Can Large Language Models Detect Rumors on Social Media?

论文信息论文标题:Can Large Language Models Detect Rumors on Social Media?论文作者:刘强、陶翔、吴俊飞、吴舒、王亮论文来源:发布时间:2024-02-06论文地址:link论文代码:link1 介绍 1.1 研究动机研究动机围…

压迫

从明天开始到考研结束前 拒绝三角洲 方舟想起来了挂一下,没想起来就算了 抖音不再打开 b站用来看课 每天总结反思 多学多想多思考 提高效率 瞄着最高分努力 看看实力 如果考的太高完全可以选择二战,这也是二战得信心…

P13573 [CCPC 2024 重庆站] Pico Park

P13573 [CCPC 2024 重庆站] Pico Park 题意: 游戏中,有 \(n\le 500\) 名玩家,依次站在数轴的 \(1,2,3, \dots, n\) 处,第 \(i\) 名玩家有一个面向的方向 \(d_i\),为向左或向右。 每名玩家手里有一把缩小枪,玩家会…

手工安装gcc-13.3.0

手工安装gcc-13.3.0wget https://gcc.gnu.org/pub/gcc/infrastructure/gmp-6.2.1.tar.bz2wget https://gcc.gnu.org/pub/gcc/infrastructure/mpfr-4.1.0.tar.bzwget https://gcc.gnu.org/pub/gcc/infrastructure/mpc-1…

深入解析:Cookie、Session、JWT、SSO,网站与 APP 登录持久化与缓存

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

gowin ide linux安装教程

1.下载,下载地址 https://www.gowinsemi.com.cn/software/index 我下载的版本是Gowin_V1.9.11.03_Education_Linux ps:云源软件和云源编程器的区别: 云源软件包含一系列工具如ide,综合工具,编程器等等 云源编程器就…

AT_arc111_f [ARC111F] Do you like query problems?

首先这个取 \(\min,\max\) 操作很不好做。 并且对可能的操作序列计数很不好做。 参考 【题解】ARC111F Do you like query problems?。 考虑先转期望,求出每种操作序列的期望结果。 发现序列中每个位置都不受其他位置…

Win7 隐藏文件夹盘符

1、打开注册表编辑器 regedit 2、找到 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\Explorer 3、 右键新建 DWORD-32 值:名称修改为:NoDrives 十进制值按下表填写4、重启系统生效!

pythontip 按条件过滤字典

编写一个程序,根据某个条件过滤字典值。 对于这个挑战,条件是字典值应该大于整数k。 定义函数filter_dict_values(),有两个参数:字典mixed_dict和整数k。 在函数内部,创建一个新字典,并从mixed_dict过滤值不是整…

DotNetGuide 突破了 9.5K + Star,一份全面的C#/.NET/.NET Core学习、工作、面试指南知识库!

DotNetGuide 介绍 DotNetGuide是一个专注于C#/.NET/.NET Core学习、工作、面试指南的 GitHub 知识库,该知识库记录、收集和总结了C#/.NET/.NET Core基础知识、学习路线、开发实战、编程技巧练习、学习视频、文章、书籍…

如何把华为mate 60手机备份到移动硬盘

如何把mate 60备份到移动硬盘华为Mate 60可通过USB OTG连接移动硬盘进行数据备份,以下是具体操作步骤: 连接移动硬盘使用USB OTG数据线将移动硬盘连接至Mate 60的Type-C接口。 确保移动硬盘已插入电源(部分硬盘需额…

Vue实例学习

vue的学习 1.初始化渲染 第一步构建用户界面,第二步创建vue实例初始化渲染 核心步骤分为4步: 1.准备容器,假设这个容器的唯一id是app 2.引包(vue的版本),vue有两种版本开发版本/生产版本 开发版本有完整的警告和…

2.2 语言处理程序基础

2.2 语言处理程序基础2.2 语言处理程序基础以编译方式翻译C/C++源程序的过程中,类型检查在( )阶段处理。 A. 词法分析 B. 语义分析 C. 语法分析 D. 目标代码生成正确答案是 B。 解析 本题考查的是编译器工作过程。…

Ai元人文:价值的“迷思”与“归真”——从家庭之爱到文明共生

Ai元人文:价值的“迷思”与“归真”——从家庭之爱到文明共生 ——Ai元人文构想 价值的“迷思”与“归真”:从家庭之爱到文明共生 第一章:困局——迷失在词语的丛林 我们生活在一个价值词汇泛滥的时代。“公平”、“…