HDOJ5616 Jam‘s balance

news2024/11/18 10:46:25

目录

  • HDOJ5616 Jam's balance
    • 题目描述
      • 背景
      • 输入
      • 输出
    • 题解
      • 解法一
      • 解法二
      • 优化
    • 打赏

HDOJ5616 Jam’s balance

题目描述

背景

N N N个已知质量的砝码,分别询问给出的 M M M个质量能否被称出

输入

  1. 第一行输入一个变量 T T T,表示有 T T T组数据;
  2. 第二行输入一个变量 N N N,表示有 N N N个砝码;
  3. 第三行输入 N N N个变量 w 1 , w 2 , ⋯   , w n w_1, w_2, \cdots, w_n w1,w2,,wn,分别表示这 N N N个砝码的质量;
  4. 第四行输入一个变量 M M M,表示要询问的质量有 M M M个;
  5. 第五行输入 M M M个变量 u 1 , u 2 , ⋯   , u m u_1, u_2, \cdots, u_m u1,u2,,um,分别表示要询问的质量

输出

对于每组数据每个询问的质量判断并输出一行 Y E S YES YES N O NO NO

题解

解法一

定义一个数组 f [ ] f[] f[] f [ i ] f[i] f[i]表示当表示出的质量小于等于 i i i时所能表示的最大值,那么本题就可以看成是一个背包问题,而且是每个物品的体积和价值都一致的背包问题
背包问题的状态转移方程是 f [ j ] = m a x ( f [ j ] , f [ j − v [ i ] ] + w [ i ] ) f[j] = max(f[j], f[j - v[i]] + w[i]) f[j]=max(f[j],f[jv[i]]+w[i]),该问题中即为 f [ j ] = m a x ( f [ j ] , f [ j − w [ i ] ] + w [ i ] ) f[j] = max(f[j], f[j - w[i]] + w[i]) f[j]=max(f[j],f[jw[i]]+w[i]),但是该问题有一些不同,因为砝码可以放在两边,所以不是简单的价值相加,但是考虑用 w [ i ] w[i] w[i]加上前 i − 1 i - 1 i1个砝码组合出的某个方案时还是可以使用这个方程的
现在考虑相减的情况,假设要用第 i i i个砝码更新 f [ j ] f[j] f[j],那么对第 i i i个砝码的使用有两种情况,一是用前 i − 1 i - 1 i1个砝码组合出的某个方案减去 w [ i ] w[i] w[i],二是用 w [ i ] w[i] w[i]减去前 i − 1 i - 1 i1个砝码组合出的某个方案
第一种情况和相加类似,易得 f [ j ] = m a x ( f [ j ] , f [ j + w [ i ] ] − w [ i ] ) f[j] = max(f[j], f[j + w[i]] - w[i]) f[j]=max(f[j],f[j+w[i]]w[i]),但是为了防止 f [ k ] ( k > j ) f[k](k > j) f[k](k>j) f [ j ] f[j] f[j]更先更新,从而导致同一砝码使用两次, j j j应该顺序枚举
第二种情况使用 w [ i ] w[i] w[i]更新时,由于 f [ j ] f[j] f[j]表示的是不大于 j j j的最大值,所以应该用 w [ i ] w[i] w[i]减去不小于 w [ i ] − j w[i] - j w[i]j的最小值,那么还需要再定义一个数组 g [ ] g[] g[] g [ i ] g[i] g[i]表示当表示出的质量大于等于 i i i时所能表示的最小值,于是可以得到方程 f [ j ] = m a x ( f [ j ] , w [ i ] − g [ w [ i ] − j ] ) f[j] = max(f[j], w[i] - g[w[i] - j]) f[j]=max(f[j],w[i]g[w[i]j])
定义了一个新的数组,那么它也需要被维护,方程和 f f f是类似的
接着考虑到实际上只需要判断是否可以得到询问的质量,而不需要真正算出最大值和最小值,所以 f , g f , g f,g都可以换为 b o o l bool bool数组并对方程做出改变
最后考虑相加和两种相减计算的先后顺序,相加第一个,之后无论是何种相减,即使减去的方案中使用了同一砝码也会抵消,第二个是相减的情况二,这样在考虑相减的情况一时遇到同一砝码也会抵消,如果后两者反过来,则会导致统一砝码使用多次
代码如下:

#include<cstdio>
#include<cstring>
using namespace std;

const int M = 25;
const int N = 2005;

int main() {
    int t, n, m, mxx, sum;
    int w[M], v[N];
    bool f[N], g[N];

    scanf("%d", &t);

    while(t--) {
        memset(f, 0, sizeof(f));
        memset(g, 0, sizeof(g));
        g[0] = 0;

        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) scanf("%d", &w[i]);
        scanf("%d", &m);
        for(int i = 1; i <= m; ++i) scanf("%d", &v[i]);

        sum = w[1];
        g[w[1]] = f[w[1]] = g[0] = f[0] = 1;     //初始化不要忘记改了

        for(int i = 2; i <= n; ++i) {
            for(int j = sum += w[i]; j > w[i]; --j) {
                f[j] |= f[j - w[i]];
                g[j] |= g[j - w[i]];
            }
            for(int j = 1; j <= w[i]; ++j) {
                f[j] |= g[w[i] - j];
                g[j] |= f[w[i] - j];
            }
            for(int j = 1, mx = sum - (w[i] << 1); j <= mx; ++j) {
                f[j] |= f[j + w[i]];
                g[j] |= g[j + w[i]];
            }
        }

        for(int i = 1; i <= m; ++i) f[v[i]] ? puts("YES") : puts("NO");
    }
    return 0;
}

解法二

接下来考虑是否可以把两种相减变成一种,核心想法还是背包问题的解法
可以考虑把相加和相减分成两个循环,先单纯考虑相加,再用方案减去 w [ i ] w[i] w[i]的方式对方案进行更新,这样就同时考虑了两种相减
代码如下:

#include<cstdio>
#include<cstring>
using namespace std;

#define il inline

const int M = 25;
const int N = 2005;

int main() {
    bool f[N];
    int t, n, m, sum;
    int w[M];

    scanf("%d", &t);

    while(t--) {
        memset(f, 0, sizeof(f));
        sum = 0, f[0] = 1;

        scanf("%d", &n);
        for(int i = 1; i <= n; ++i) scanf("%d", &w[i]);

        for(int i = 1; i <= n; ++i) {
            for(int j = sum + w[i]; j >= w[i]; --j) f[j] |= f[j - w[i]];
            sum += w[i];
        }

        for(int i = 1; i <= n; ++i) {
            int mx = sum - w[i];
            for(int j = 1; j <= mx; ++j) f[j] |= f[j + w[i]];
        }

        scanf("%d", &m);
        while(m--) {
            int x;
            scanf("%d", &x);
            f[x] ? puts("YES") : puts("NO");
        }
    }
    return 0;
}

优化

当同质量的砝码有多个时,如果看成单独的多个砝码,某些本质相同的操作会重复进行,所以不妨看成多重背包再进行二进制优化,二进制优化就是把有多个的某质量砝码依据二进制进行分组等效,如把 8 8 8个质量为 1 1 1的砝码等效质量分别为 1 , 2 , 4 , 1 1 , 2 , 4 , 1 1,2,4,1 4 4 4个砝码,等效前后能表示的质量不变,而砝码数量却下降了
可以发现这个等效本质上就是满 3 3 3 1 1 1,同时两倍质量的砝码数量加 1 1 1,直到不再可以等效
由于可能的砝码数量只有 0 , 1 , 2 0 , 1 , 2 0,1,2,所以不妨使用 b o o l bool bool数组 s s s储存砝码数量, f a l s e , t r u e false , true false,true分别表示 1 , 2 1 , 2 1,2,且 w [ i ] w[i] w[i]表示第 i i i种砝码的质量,再添加一个数组 p o s [ i ] pos[i] pos[i]表示质量为 i i i的砝码在 s s s中的下标,这样每当出现一个新质量的砝码, s s s中元素的总数 t o t tot tot就加 1 1 1
由于数量较小,对于数量为 2 2 2的砝码也可以不采用多重背包的一般方法,直接当成 2 2 2个单独砝码处理即可
代码如下:

#include<cstdio>
#include<cstring>
using namespace std;

#define il inline

const int M = 25;
const int N = 2005;

il int read() {
    int x = 0;
    char c = getchar();
    while(c > '9' || c < '0') c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
    return x;
}

int main() {
    bool s[M], f[N];
    int t, n, m, tot, sum, wei;
    int w[M], pos[N];
    memset(pos, 0, sizeof(pos));

    t = read();

    while(t--) {
        memset(f, 0, sizeof(f));
        tot = sum = 0, f[0] = 1;

        n = read();
        for(int i = 1, j; i <= n; ++i) {
            wei = read(), j = pos[wei];
            while(j && s[j]) s[j] = 0, j = pos[wei <<= 1];
            if(!j) pos[wei] = ++tot, s[tot] = 0, w[tot] = wei;
            else s[j] = 1;
        }

        for(int i = 1; i <= tot; ++i) {
            for(int j = sum += w[i]; j >= w[i]; --j) f[j] |= f[j - w[i]];
            if(s[i]) w[++tot] = w[i], s[tot] = 0;
            pos[w[i]] = 0;     //顺便重置pos,这样就不用反复memset
        }

        for(int i = 1; i <= tot; ++i)
            for(int j = 1, mx = sum - w[i]; j <= mx; ++j)
            	f[j] |= f[j + w[i]];

        m = read();
        while(m--) f[read()] ? puts("YES") : puts("NO");
    }
    return 0;
}

打赏

制作不易,若有帮助,欢迎打赏!
赞赏码

支付宝付款码

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

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

相关文章

二轮平衡车直立控制VREP仿真及python上位机监控实现基础环境

目录 1.引言2. 实验流程2.1. V-REP环境搭建2.2. 电机和部件建模 3.控制策略描述3.1. PID控制3. 2. 控制参数调整 4.代码结构5.结论配套环境和源代码PS.扩展阅读ps1.六自由度机器人相关文章资源ps2.四轴机器相关文章资源ps3.移动小车相关文章资源 1.引言 在机器人学和自动化领域…

东南亚本地化游戏

通常&#xff0c;亚洲电子游戏市场首先与中国联系在一起。但最近&#xff0c;分析人士越来越关注一个邻近地区&#xff1a;东南亚。而且有充分的理由。 该地区包括中南半岛、马来群岛和邻近岛屿上的十一个国家。1967年&#xff0c;其中10个国家&#xff08;除东帝汶外&#xf…

.NET C# 使用GDAL将mdb转换gdb数据

.NET C# 使用GDAL将mdb转换gdb数据 目录 .NET C# 使用GDAL将mdb转换gdb数据1 环境2 Nuget3 Code 1 环境 VisualStudio2022 .NET6 GDAL 3.8.5 2 Nuget 3 Code FeatureExtension.cs public static class FeatureExtension {[DllImport("gdal.dll", EntryPoint &…

各大广告商竞相厮杀下,诞生了一个偏门的副业方式

前段时间&#xff0c;想买摩托车&#xff0c;但是媳妇不让买&#xff0c;所以我打算偷偷买&#xff0c;然后萌生了去摆摊赚钱的想法&#xff0c;但是还没有实施就在网上接触到了“某赚”APP&#xff0c;于是一发不可收拾&#xff0c;用我的话来说&#xff0c;我做的不是副业&am…

从一万英尺外看libevent(源码刨析)

从一万英尺外看libevent 温馨提示&#xff1a;阅读时间大概二十分钟 前言 Libevent是用于编写高速可移植非阻塞IO应用的库&#xff0c;其设计目标是&#xff1a; 可移植性&#xff1a;使用libevent编写的程序应该可以在libevent支持的所有平台上工作。即使没有好的方式进行非…

多业态、多品牌企业,如何实现积分通积通兑?(附大会员方案)

2021年&#xff0c;龙湖升级珑珠为全业态通用积分&#xff0c;招商荟深度接入招商蛇口大会员体系建设&#xff1b;2022年&#xff0c;华润置地大会员“万象星”正式上线&#xff1b;2023年&#xff0c;“蒙牛生活家会员中心”全新上线…… 越来越多地产、零售等行业的集团品牌…

【学习】如何利用Python技术进行软件测试相关工作

Python是一种广泛使用的高级编程语言&#xff0c;它因其简洁的语法、强大的库支持和跨平台特性而受到开发者的喜爱。在软件测试领域&#xff0c;Python同样发挥着重要作用&#xff0c;它可以帮助测试人员编写自动化测试脚本、进行接口测试、性能测试、以及处理测试数据等。以下…

迅为RK3588开发板支持LVDS信号,标准 HDMI信号,IMIPI信号

性能强--iTOP-3588开发板采用瑞芯微RK3588处理器&#xff0c;是全新一代ALoT高端应用芯片&#xff0c;采用8nm LP制程&#xff0c;搭载八核64位CPU&#xff0c;四核Cortex-A76和四核Cortex-A55架构&#xff0c;主频高达2.4GHZ&#xff0c;8GB内存&#xff0c;32GB EMMC。 四核心…

2024第十三届中国PMO大会主持人介绍

全国PMO专业人士年度盛会 由PMO评论主办的2024第十三届中国PMO大会邀请了到十几位知名企业的PMO和项目管理专家来担任大会主持人。大会将于6月29-30日在北京举办&#xff0c;敬请关注&#xff01; 主持人介绍 肖杨&#xff0c;国际知名组织级项目管理专家&#xff0c;微薄之力…

[深度学习] 自编码器Autoencoder

自编码器&#xff08;Autoencoder&#xff09;是一种无监督学习算法&#xff0c;主要用于数据的降维、特征提取和数据重建。自编码器由两个主要部分组成&#xff1a;编码器&#xff08;Encoder&#xff09;和解码器&#xff08;Decoder&#xff09;。其基本思想是将输入数据映射…

软考《信息系统运行管理员》-1.2信息系统运维

1.2信息系统运维 传统运维模式&#xff08;软件&#xff09; 泛化&#xff1a;软件交付后围绕其所做的任何工作纠错&#xff1a;软件运行中错误的发现和改正适应&#xff1a;为适应环境做出的改变用户支持&#xff1a;为软件用户提供的支持 新的不同视角下的运维 “管理”的…

【八股系列】Vue中的<keep-alive>组件:深入解析与实践指南

&#x1f389; 博客主页&#xff1a;【剑九 六千里-CSDN博客】 &#x1f3a8; 上一篇文章&#xff1a;【探索响应式布局的奥秘&#xff1a;关键技术与实战代码示例】 &#x1f3a0; 系列专栏&#xff1a;【面试题-八股系列】 &#x1f496; 感谢大家点赞&#x1f44d;收藏⭐评论…

三、用户中心项目笔记----后端多环境实战+原始部署

后端多环境主要是修改&#xff1a; 依赖的环境地址 数据库地址 缓存地址 消息队列地址 项目端口号 服务器配置 后端怎么去区分不同的环境&#xff1f; 我们后端的SpringBoot项目&#xff0c;通过application.yml添加不同后缀来区分配置文件 application.yml就是公共的配置&a…

NeRF从入门到放弃6:两种OpenCV去畸变模型

针孔相机和鱼眼相机的去畸变模型是不一样的。 针孔相机的畸变参数有12个&#xff0c;k1~k6是径向畸变参数&#xff0c;p1 p2是切向畸变&#xff0c;s1s4&#xff1b;而鱼眼相机是等距模型&#xff0c;畸变参数只有4个k1k4。 针孔相机 畸变分为径向畸变和切向畸变。 把相机平…

链式结构二叉树练习

一.二叉树的前序遍历 想要输出所给值&#xff0c;就要先用数组将数据存储起来&#xff0c;所以这里我们单独创建一个前序遍历函数&#xff0c;将所要数据前序遍历并放入数组&#xff0c;代码如下&#xff1a; void preOrder(struct TreeNode* root, int* a, int* pi)//前序遍历…

新鲜出炉的信息化一机两用方案

在信息化日益发展的今天&#xff0c;网络安全问题愈发凸显其重要性。尤其是在政府和企事业单位中&#xff0c;如何在保证业务流畅和工作效率的同时&#xff0c;确保信息高安全性&#xff0c;成为了一个亟待解决的问题。而“一机两用”政策&#xff0c;正是针对这一需求而提出的…

如何理解:业务架构、应用架构、数据架构、技术架构与系统和复杂度

关于系统的理解 1.1 系统的概述 随着人类社会的发展&#xff0c;人们面对越来越多的规模巨大、关系复杂、参数众多地复杂问题&#xff0c;这些问题的复杂度已经远远超出人类的理解能力&#xff0c;系统论就是为了分析和解决这些问题而生。我们平时接触的计算机系统包括软件系统…

C语言 | 文件操作(下)【必收藏】

文件操作&#xff08;下&#xff09; 5、文件的顺序读写5.1 顺序读写函数介绍5.1.1 fputc与fgetc5.1.2 fputs与fgets5.1.3 fprintf与fscanf5.1.4 fread与fwrite 5.2 对比一组函数 6. 文件的随机读写6.1 fseek6.2 ftell6.3 rewind 7. 文件读取结束的判定7.1 被错误使用的feof 8.…

Python爬虫实战:利用代理IP批量下载哔哩哔哩美女视频

文章 目录 1.前言2.爬取目标3.准备工作3.1 环境安装3.2 代理免费获取 四、爬虫实战分析4.1 翻页分析4.2 获取视频跳转链接4.3 下载视频4.4 视频音频合并4.5 完整源码 五、总结 1.前言 粉丝们&#xff08;lsp&#xff09;期待已久的Python批量下载哔哩哔哩美女视频教程它终于来…

Java中File文件和IO流

File文件和IO流 概述 FIle是java.io.下面的包&#xff0c;用于代表当前操作系统的文件 可以获文件信息&#xff0c;判断文件类型&#xff0c;创建删除文件夹 注意&#xff1a;File只能对文件本身进行操作&#xff0c;不能读写文件里面存储的数据 …