POJ 3977 Subset 折半枚举+二分搜素+双指针

一、题目大意

我们有N(N<=35)个元素,从中选取一个子集,使得它的元素求和的绝对值最小,如果有多个可行解,选择元素最小的。

输出最优子集的元素总和绝对值,和最优子集元素的数量。

二、解题思路

我们把前一半,后一半数组分开考虑。

我们利用二进制递增的思路(0001,0010,0011...1111),把后一半数组的所有子集求和给算出来(去掉空集),同时记录每个子集的元素数量。

之后根据子集的sum进行排序,然后利用双指针,把所有sum相等的子集的元素数量更新为 同等sum下最小的元素数量。

然后利用二进制枚举前半部分数组(包括空集),对于每一个左半部分的元素和leftSum,去后半部分数组里二分找 -leftSum,(这个二分的思想就是找到后半部分最小子集元素和不小于 -leftSum的第一个下标),然后把二分的结果idx和idx-1都判断下,计算左右子集和的绝对值,和元素数量。更新ans。

需要注意的是我们去掉了右边为空集的情况,所以要额外判断下只使用左边元素的情况。

我也是很菜了,这个题目WA了60多次,写了6天,最后绝对不用pair了,自己写结构体,也不用lower_bound了,自己写二分,然后再结合自己想出来的尺取法,过了这道题。

过程中没有查看题解,没有搜过答案,但是去看了STL中pair的源码和Comparator的源码,还看了下《挑战程序设计》的“超大背包问题”的源码,其实不应该去看这些,影响到了进步和思考的进程,看完STL源码和白书后决定不用pair和lower_bound了,手写二分底层实现,手写双指针优化,终究是过了。

可以说我是非常菜了,自己摸爬滚打总结的一套代码分享在下面。

过了以后查过题解,我这个他们那个map那个复杂一些,因为用了自定义结构体,双指针和手写二分底层实现,但是比它快了5倍吧,源码分享给大家。

三、代码

#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
struct Node
{int cnt;ll sum;Node(ll sum = 0LL, int cnt = 0) : sum(sum), cnt(cnt) {}
};
Node rightNodes[262150];
int towPow[27], n, rightLen, leftLen, rightPow, leftPow, ansCnt;
ll num[40], ans, inf = 0x3f3f3f3f3f3f3f3fLL;
void initTwoPow()
{towPow[0] = 1;for (int i = 1; i <= 21; i++){towPow[i] = towPow[i - 1] * 2;}
}
bool compareNode(const Node &a, const Node &b)
{return a.sum < b.sum;
}
ll absVal(ll a)
{if (a >= 0LL){return a;}else{return a * (-1LL);}
}
void input()
{ans = 0LL;for (int i = 0; i < n; i++){scanf("%lld", &num[i]);ans = ans + num[i];}ans = absVal(ans);ansCnt = n;leftLen = n / 2;rightLen = n - leftLen;leftPow = towPow[leftLen];rightPow = towPow[rightLen];
}
void calcRightSubsetBesideEmptySet()
{for (int i = 1; i < rightPow; i++){rightNodes[i - 1].sum = 0LL;rightNodes[i - 1].cnt = 0;for (int j = 0; j < rightLen; j++){if ((i & towPow[j]) == towPow[j]){rightNodes[i - 1].sum = rightNodes[i - 1].sum + num[leftLen + j];rightNodes[i - 1].cnt = rightNodes[i - 1].cnt + 1;}}}rightNodes[rightPow - 1].sum = inf;rightNodes[rightPow - 1].cnt = n + 1;sort(rightNodes, rightNodes + rightPow, compareNode);
}
void minimizeCntByTwoPosinter()
{int l = 0, r = 1, optCnt = -1;while (true){while (r < rightPow && rightNodes[r].sum != rightNodes[l].sum){l++;r++;}optCnt = rightNodes[l].cnt;while (r < rightPow && rightNodes[r].sum == rightNodes[l].sum){optCnt = min(optCnt, rightNodes[r].cnt);r++;}while ((l + 1) < r){rightNodes[l++].cnt = optCnt;}if (r == rightPow){break;}}
}
int binarySearch(ll leftSum)
{int l = -1, r = rightPow;while (l + 1 < r){int mid = (l + r) / 2;if (rightNodes[mid].sum < leftSum){l = mid;}else{r = mid;}}return (l + 1);
}
void solve()
{ll lSum = 0LL;int lCnt = 0;for (int i = 0; i < leftPow; i++){lSum = 0LL;lCnt = 0;for (int j = 0; j < leftLen; j++){if ((i & towPow[j]) == towPow[j]){lSum = lSum + num[j];lCnt = lCnt + 1;}}if (lCnt != 0 && absVal(lSum) < ans){ans = absVal(lSum);ansCnt = lCnt;}else if (lCnt != 0 && absVal(lSum) == ans && lCnt < ansCnt){ansCnt = lCnt;}int idx = binarySearch(lSum * (-1LL));if ((idx + 1) < rightPow && absVal(rightNodes[idx].sum + lSum) < ans){ans = absVal(rightNodes[idx].sum + lSum);ansCnt = rightNodes[idx].cnt + lCnt;}else if ((idx + 1) < rightPow && absVal(rightNodes[idx].sum + lSum) == ans && (rightNodes[idx].cnt + lCnt) < ansCnt){ansCnt = rightNodes[idx].cnt + lCnt;}idx--;if (idx >= 0 && absVal(rightNodes[idx].sum + lSum) < ans){ans = absVal(rightNodes[idx].sum + lSum);ansCnt = rightNodes[idx].cnt + lCnt;}else if (idx >= 0 && absVal(rightNodes[idx].sum + lSum) == ans && (rightNodes[idx].cnt + lCnt) < ansCnt){ansCnt = rightNodes[idx].cnt + lCnt;}}
}
int main()
{initTwoPow();while (true){scanf("%d", &n);if (n == 0){break;}input();calcRightSubsetBesideEmptySet();minimizeCntByTwoPosinter();solve();printf("%lld %d\n", ans, ansCnt);}return 0;
}

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

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

相关文章

JavaWeb 学习笔记 7:Filter

JavaWeb 学习笔记 7&#xff1a;Filter 1.快速开始 使用过滤器的方式与 Servlet 类似&#xff0c;要实现一个Filter接口&#xff1a; WebFilter("/*") public class FirstFilter implements Filter {public void init(FilterConfig filterConfig) throws ServletE…

多输入多输出 | MATLAB实现GA-BP遗传算法优化BP神经网络多输入多输出

多输入多输出 | MATLAB实现GA-BP遗传算法优化BP神经网络多输入多输出 目录 多输入多输出 | MATLAB实现GA-BP遗传算法优化BP神经网络多输入多输出预测效果基本介绍程序设计往期精彩参考资料 预测效果 基本介绍 多输入多输出 | MATLAB实现GA-BP遗传算法优化BP神经网络多输入多输出…

《向量数据库指南》——向量搜索库Faiss 迁移到 Milvus 2.x

Faiss -> Milvus 2.x 1. Faiss 数据准备 前提条件是用户已经准备好了自己的 faiss 数据文件。(为了能快速体验,在项目源码的 testfiles 目录下放置了 faiss 测试数据方便用户体验: faiss_ivf_flat.index. 2. 编译打包 这部分同上,不再展开介绍。 3. 配置 migration.ymal…

vue下载Excel文件

前端vue实现导出Excel文件 用到的是 上代码 var wb XLSX.utils.table_to_book(document.querySelector(#my-table));//关联dom节点 这个是表格绑定的id名称var wbout XLSX.write(wb, {bookType: xlsx,bookSST: true,type: array})try {FileSaver.saveAs(new Blob([wbout], {…

【教学类】公开课学号挂牌(15*15CM手工纸)

作品展示&#xff1a; 15*15CM手工纸 文本框12磅加粗。学号数字是段落写入&#xff0c;黑体270磅 背景需求 最近都在小班、中班、大班里做“Python学具测试”&#xff0c;由于都是陌生的孩子&#xff0c;上课时&#xff0c;我通常只能喊“白衣服的女孩”“花格子衣服的男孩”…

精华回顾:Web3 前沿创新者在 DESTINATION MOON 共话未来

9 月 17 日&#xff0c;由 TinTinLand 主办的「DESTINATION MOON: Web3 Dev Summit Shanghai 2023」线下活动在上海黄浦如约而至。 本次 DESTINATION MOON 活动作为 2023 上海区块链国际周的 Side Event&#xff0c;设立了 4 场主题演讲与 3 个圆桌讨论&#xff0c;聚集了诸多…

strtok()函数的使用方法

strtok() 函数用于将字符串分割成子字符串&#xff08;标记&#xff09;。它在 C 语言中非常常用&#xff0c;可以通过指定分隔符来拆分原始字符串&#xff0c;并依次返回每个子字符串。 以下是 strtok() 函数的使用方法&#xff1a; #include <stdio.h> #include <…

Python方法汇总:轻松实现功能!

在爬虫开发中&#xff0c;有时需要模拟登录网站以获取更多的数据或执行特定的操作。本文将为你总结几种常用的Python爬虫模拟登录方法&#xff0c;帮助你轻松实现登录功能&#xff0c;让你的爬虫更加强大有用。 一、基于Requests库的模拟登录 1. 使用Session对象&#xff1a;…

UOS Deepin Ubuntu Linux 开启 ssh 远程登录

UOS Deepin Ubuntu Linux 开启 ssh 远程登录 打开控制台 安装 openssh-server sudo apt -y install openssh-server修改 /etc/ssh/ssh_config 文件 sudo vim /etc/ssh/ssh_config找到 # Port 22 去掉 # 注释后 保存 重启 ssh 服务 sudo systemctl restart ssh设置 ssh 服务 开机…

ros开发中编译cpp文件的2个办法

方式一&#xff1a; 在Ubuntu控制台输入指令 cd catkin_ws 进入到工作空间 然后在输入&#xff1a; catkin_make --pkg catkin_practice 注释&#xff1a;以上catkin_ws是工作空间名称&#xff0c;catkin_practice是工作空间中将要被编译的包的名称 方式二&#xff1a; …

前端控制小数点精度及数字千位分割

前端控制小数点精度及数字千位分割&#xff0c;表头居中&#xff0c;每行单元格内容居右。 前端控制小数点精度&#xff1a; <el-table-column prop"cycz" label"差异产值" header-align"center" align"right"><template s…

JVM高级性能调试

标准的JVM是配置为了高吞吐量&#xff0c;吞吐量是为了科学计算和后台运行使用&#xff0c;而互联网商业应用&#xff0c;更多是为追求更短的响应时间&#xff0c;更低的延迟Latency&#xff08;说白了就是更快速度&#xff09;&#xff0c;当用户打开网页没有快速响应&#xf…

安卓机型-MTK芯片掉串码 掉基带 如何用工具进行修复 改写参数

在早期MTK芯片机型中较多使用AP BP方式来修复mtk芯片机型的串码。目前MTK机型对于丢基带 掉串码问题大都使用MODEM META工具来进行修复串码或者改写参数。今天以一款mtk芯片机型来做个演示&#xff0c; 高通芯片类的可以参考; 高通改串相关 工具仅支持在联发科芯片组上运行的…

WEB使用VUE3实现地图导航跳转

我们在用手机查看网页时可以通过传入经纬度去设置目的地然后跳转到对应的地图导航软件&#xff0c;如果没有下载软件则会跳转到下载界面 注意&#xff1a; 高德地图是一定会跳转到一个新网页然后去询问用户是否需要打开软件百度和腾讯地图是直接调用软件的这个方法有缺陷&…

iOS——引用计数(一)

自动引用计数 自动引用计数&#xff08;ARC&#xff0c;Automatic Reference Counting&#xff09;是指内存管理中对引用采取自动计数的技术。 满足以下要求后&#xff0c;我们的代码就无需再次键入retain或者是release代码了&#xff1a; 使用Xcode 4.2或以上版本使用LLVM编…

Android 启动优化案例:WebView非预期初始化排查

去年年底做启动优化时&#xff0c;有个比较好玩的 case 给大家分享下&#xff0c;希望大家能从我的分享里 get 到我在做一些问题排查修复时是怎么看上去又low又土又高效的。 1. 现象 在我们使用 Perfetto 进行app 启动过程性能观测时&#xff0c;在 UI 线程发现了一段 几十毫…

Mybatis分页框架-PageHelper

Mybatis分页框架-PageHelper 一、PageHelper基础使用1.引入jar包2.配置conifg3.测试使用 二、PageHelper的多种用法1.使用PageHelper.startPage传入对象2.不使用PageHelper.startPage,而使用PageHelper.offsetPage3.使用Lambda进行分页4.不使用PageHelper直接分页5.想要使用分页…

Google拟放弃博通自行研发AI芯片 | 百能云芯

谷歌计划自行研发人工智能&#xff08;AI&#xff09;芯片&#xff0c;考虑将博通&#xff08;Broadcom&#xff09;从其供应商名单中剔除&#xff0c;但谷歌强调双方的合作关系不会受到影响。 根据美国网络媒体《The Information》的报道&#xff0c;谷歌高层正在讨论可能在20…

【计算机网络】图解路由器(二)

本系列包含&#xff1a; 图解路由器&#xff08;一&#xff09;图解路由器&#xff08;二&#xff09; 图解路由器&#xff08;二&#xff09; 21、什么是静态路由&#xff1f;22、什么是动态路由&#xff1f;23、动态路由有哪些类型&#xff1f;24、什么是 RIP &#xff1f;2…

Python教程(14)——Python函数的入门学习

函数是什么&#xff1f;在编程中&#xff0c;函数是一段可重用的代码块&#xff0c;用于完成特定任务或执行特定操作。它可以接输入参数并返回一个值或执行一系列操作。函数可以帮助程序员将代码模块化&#xff0c;提高代码的可读性和可维护性。 函数通常包括以下组成部分&…