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

news2024/12/24 2:08:03

一、题目大意

我们有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.coloradmin.cn/o/1033517.html

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

相关文章

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

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

窜货采买第三方怎么选择

窜货溯源服务听起来并不难&#xff0c;无非就是买货&#xff0c;但是否能买到货&#xff0c;同时在买到之后能否顺利完成溯源工作&#xff0c;也是非常有学问的&#xff0c;很多品牌会选择第三方服务商进行采买合作&#xff0c;这样可以规避品牌自己操作时的不合规性&#xff0…

Exploit-DB 使用小结

Exploit-DB &#xff08;网址&#xff1a;https://www.exploit-db.com&#xff09; 是一个漏洞库网站 &#xff0c;存储了大量的漏洞利用程序&#xff0c;可以帮助安全研究者和渗透测试工程师更好的进行安全测试工作&#xff0c;目前是世界上公开收集漏洞最全的数据库&#xff…

CNN(八):Inception V1算法实战与解析

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客 &#x1f356; 原作者&#xff1a;K同学啊|接辅导、项目定制 1 Inception V1 Inception v1论文 1.1 理论知识 GoogLeNet首次出现在2014年ILSVRC比赛中获得冠军。这次的版本通常称其为Inception V1。…

QtCreator报大量未知标识符错误的解决方法

目录 前言背景介绍问题1问题1解决方法问题2问题2 解决方法总结 前言 本文记录了在使用QtCreator开发时遇到的一个错误&#xff0c;导致编译时出现大量的“未知标识符”&#xff0c;经过一番努力最终解决了这个问题&#xff0c;特在此记录。 背景介绍 Qt项目在麒麟V10 系统下…

Linux中创建用户要自己设置密码

因为不知道Linux默认设置的密码&#xff0c;没办法接下来愉快的使用。如下&#xff1a; 而想要新建Linux用户密码&#xff0c;请您执行以下步骤: . 1.打开终端并以root身份登录; 2.输入命令“useradd username",其中username为您新建的用户名; 3.使用命令“passwd usernam…

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

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

【数据结构】顺序查找,折半查找,分块查找的知识点总结及相应的代码实现

目录 1、顺序查找 定义及步骤 代码实现 2、折半查找 定义及步骤 代码实现 折半查找判定树 3、分块查找 定义及步骤 1、顺序查找 定义及步骤 顺序查找的定义&#xff1a;从数据集合的起始位置开始&#xff0c;逐一比较每个数据元素&#xff0c;直到找到所要查找…

百度SEO优化不稳定的原因分析(提升网站排名的稳定性)

百度SEO优化不稳定介绍蘑菇号-www.mooogu.cn SEO不稳定是指网站在搜索引擎中的排名不稳定&#xff0c;随着时间的推移会发生变化。这种情况可能会出现在网站页面结构、内容质量、外链质量等方面存在缺陷或不合理之处。因此&#xff0c;优化SEO非常重要&#xff0c;可以提高网站…

4+机器学习+实验验证

今天给同学们分享一篇4机器学习实验验证的生信文章“Identification and Analysis of Neutrophil Extracellular Trap-Related Genes in Osteoarthritis by Bioinformatics and Experimental Verification”&#xff0c;这篇文章于2023年8月31日发表在 J Inflamm Res 期刊上&am…

两个数使用JavaScript比较大小;JavaScript知识点

一、两个数使用JavaScript比较大小代码 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head><body><script type"text/javascript">var aprompt("请输入第一个数:"…

Web自动化测试 —— 如何进行Selenium页面数据及元素交互?啊哈

前言&#xff1a; Web自动化测试是一种常用的测试方式&#xff0c;通过在浏览器中模拟用户操作以及与页面元素的交互&#xff0c;可以有效地检验页面的功能性以及稳定性。Selenium是一款流行的Web自动化测试工具&#xff0c;在本篇文章中&#xff0c;我们将介绍如何使用Seleni…

青大数据结构【2022】

关键字&#xff1a; next数组、下三角矩阵、完全二叉树结点、静态分布动态分布、迪杰斯特拉最短路径、二叉排序树失败ASL、排序比较、二叉排序树中序遍历、链表删除最大值 一、单选 二、简答 三、应用 四、算法分析 五、算法设计

nginx反向代理vue项目

文章目录 前言一、创建站点1.添加站点2.添加ssl证书 二、反向代理vue项目1.添加反向代理2.更改vue项目配置3.修改反向代理配置 前言 项目描述&#xff1a;前端vue项目、后端Java项目、首页WordPress项目 客户要求&#xff1a;使用宝塔进行部署 需求描述&#xff1a;客户只有一…

【Java】Servlet API

Servlet API HttpServlet核心方法Servlet生命周期 HttpServletRequest核心方法 HttpServletResponse核心方法 HttpServlet 我们写 Servlet 代码的时候, 首先第一步就是先创建类, 继承自 HttpServlet, 并重写其中的某些方法. 核心方法 方法名称调用时机init在 HttpServlet 实…

李宏毅机器学习第一课

机器学习就是让机器找一个函数f&#xff0c;这个函数f是通过计算机找出来的 如果参数少的话&#xff0c;我们可以使用暴搜&#xff0c;但是如果参数特别多的话&#xff0c;我们就要使用Gradient Descent Regression (输出的是一个scalar数值) Classification &#xff08;在…

美团2024届秋招笔试第一场编程[汇总](上课口胡一下)

一.小美的好矩阵 口胡&#xff1a;模拟题&#xff0c;数据和题意灰常清楚。 俩层循环枚举每个3&#xfe61;3的小矩阵&#xff0c;然后枚举每个小矩阵&#xff0c;12个if判断俩俩相邻的字符是否相等。这里有个技巧&#xff1a;拿出中间的字符&#xff0c;这样就能使用一个偏移…

基于紫光同创FPGA的图像采集及AI加速

本原创文章由深圳市小眼睛科技有限公司创作&#xff0c;版权归本公司所有&#xff0c;如需转载&#xff0c;需授权并注明出处 适用于板卡型号&#xff1a; 紫光同创PGL50H开发平台&#xff08;盘古50K开发板&#xff09; 本篇优秀作品&#xff1a;2023集创赛全国总决赛紫光同…

Lua学习笔记:词法分析

前言 本篇在讲什么 Lua的词法分析 本篇需要什么 对Lua语法有简单认知 对C语法有简单认知 依赖Visual Studio工具 本篇的特色 具有全流程的图文教学 重实践&#xff0c;轻理论&#xff0c;快速上手 提供全流程的源码内容 ★提高阅读体验★ &#x1f449; ♠ 一级标题…

stm32之PWM呼吸灯

呼吸灯是灯从渐亮到渐灭周而复始形成的一个效果。由于51没有PWM所以需要定时器模拟PWM才能实现呼吸灯的效果&#xff0c;但是stm32的通用定时器是有PWM模式的&#xff0c;所以不需要再用软件模拟&#xff0c;精准度也高。 本实验用的基于stm32f103C8t6。在PB8引脚上接了一个le…