【基础算法】穷举法

news2024/11/15 11:42:56

穷举法Exhaustive method是使用最广泛、设计最简单,同时最耗时的算法,也被称为暴力法、蛮力法Brute force method

两数之和


给定一个整数数组array和一个目标值target,请在数组中找出和为目标值target的两个整数,并输出它们在数组array中的下标。


我们可以通过如下代码尝试解决:

#include<iostream>
//数组指针,数组元素个数,目标值
void outputIndexOfArray(int* array, int length, int target) {
    bool result = false;
    for (int index_1 = 0; index_1 < length; index_1++) {
        for (int index_2 = 0; index_2 < length; index_2++) {
            if (index_1 != index_2 && array[index_1] + array[index_2] == target) {
                std::cout << "array[" << index_1 << "]+array[" << index_2 << "]=" << target << std::endl;
                result = true;
            }
        }
    }
    if (!result) {
        std::cout << "无解" << std::endl;
    }
}

int main() {
    int array[10];
    for (int i = 0; i < 10; i++) {
        array[i] = i;
    }
    outputIndexOfArray(array, sizeof(array) / sizeof(array[0]), 10);
}

这个代码不难理解,需要注意的是:
C++中函数参数为数组时,函数内无法使用sizeof(array)/sizeof(array[0])的方法获取数组长度,这种方法得到的是指针大小/元素大小
需要额外的参数来传递数组大小。
运行之后我们会发现第一个结果和最后一个结果是相同的,空间中存在重复的解。

将这个题的解集用二维数组表示,就能看出问题所在。


这是一个角对称矩阵,矩阵中(i,j)(j,i)表示的组合是等价的。
我们将本题的解空间缩小到这个矩阵的上对角矩阵或下对角矩阵(不包含对角线),这样得到的结果就不会有重复了。
在代码实现上,只要将内层循环变量index_2的起始值从原来的0改为index_1+1,就可以将搜索范围限定到上对角矩阵,解空间就变为原来的一半,同时不再需要index_1!=index_2的判断。
改进后的算法实现如下:

#include<iostream>
//数组指针,数组元素个数,目标值
void outputIndexOfArray(int* array, int length, int target) {
	bool result = false;
	for (int index_1 = 0; index_1 < length; index_1++) {
		for (int index_2 = index_1 + 1; index_2 < length; index_2++) {
			if (array[index_1] + array[index_2] == target) {
				std::cout << "array[" << index_1 << "]+array[" << index_2 << "]=" << target << std::endl;
				result = true;
			}
		}
	}
	if (!result) {
		std::cout << "无解" << std::endl;
	}
}

int main() {
	int array[10];
	for (int i = 0; i < 10; i++) {
		array[i] = i;
	}
	outputIndexOfArray(array, sizeof(array) / sizeof(array[0]), 10);
}

压缩了问题的解空间后,之前的重复结果被消除,运行结果符合预期。
在应用穷举法解决问题时,关键是划定好问题的解空间。如果解空间的范围定得过大,那么不但会增加冗余的搜索操作,还可能导致结果重复;如果解空间的范围定得过小,则可能漏掉一部分解,违背了穷举法牺牲时间换取解的全面性的初衷。

百钱百鸡


我国古代数学家张丘建在《算经》一书中提出了著名的“百钱百鸡”问题:
鸡翁一,值钱五;鸡母一,值钱三;鸡雏三,值钱一。百钱买百鸡,则翁、母、雏各几何?


可将问题抽象成以下方程组:
{ 5 x + 3 y + 1 3 z = 100 x + y + z = 100 \begin{cases} 5x+3y+\frac{1}{3}z=100\\ x+y+z=100 \end{cases} {5x+3y+31z=100x+y+z=100
该方程组中包含三个未知数,仅有两个方程,是一个三元一次不定方程组。根据代数基本定理,该方程组会有无数组解,但是本题存在约束条件,所以它的解是一定有限的。
由于整型在计算时会自动舍弃小数部分,这样会造成误差,从而影响最终的结果,我们需要对第一个方程两边同时乘上三。

#include<iostream>
void buyChicken() {
	for (int x = 0; x <= 100; x++) {
		for (int y = 0; y <= 100; y++) {
			for (int z = 0; z <= 100; z++) {
				if (x + y + z == 100 && 15 * x + 9 * y + z == 300) {
					std::cout << "cock:" << x << "hen:" << y << "chick:" << z << std::endl;
				}
			}
		}
	}
}

int main() {
	buyChicken();
}

Meziak的砝码问题


一位商人有一个质量为40磅的砝码,一天,他不小心将这个砝码摔成了4块。吝啬的商人不愿意扔掉这个破碎的砝码,于是他仔细研究这4块砝码碎片,发现恰好每块砝码碎片的质量都是整数,而且各不相同,同时这4块砝码碎片可以在天平上称出1-40磅的任意整数磅。求出这4块砝码碎片的质量各是多少。


从题目给出的已知条件,我们可以总结出以下几点:

  1. 四块砝码碎片的质量都是整数,并且质量之和为10磅。
  2. 砝码碎片的质量各不相同。
  3. 四块砝码碎片可以在天平上称出1~40磅任意整数磅的质量。

所以这个问题的解空间是有限的、可列的。我们只需要划定一个合理的解空间,并在这个解空间中搜索出满足以上三个条件的解。

消除冗余解

与两数之和问题一样,组合(1,2,3,4)(4,3,2,1)是等价的,所以只需要考虑碎片的质量各是多少,不需要考虑碎片的排列方式。我们需要划定一个不重、不漏的解空间。
“两数之和”问题的解空间是一个二维矩阵,而本题的解空间是一个四维向量,可将上面的算法类比推广到思维向量解空间的问题上:

void MeziakWeight() {
	int a, b, c, d;
	for (a = 1; a < 40; a++) {
		for (b = a + 1; b < 40; b++) {
			for (c = b + 1; c < 40; c++) {
				for (d = c + 1; d < 40; d++) {
					//是否质量之和等于40
					//是否可以称出1~40任意整数磅的质量
				}
			}
		}
	}
}

这里将内层循环的变量bcd的起点从原来的1改为上一层循环变量值加1,这样既可以清除解空间中的冗余解,又可以在判断结果时省略对“砝码碎片的质量各不相同”这一条件的判断,因为循环的最内层得到的abcd一定不相等。

任意整数磅


将碎片a和碎片c放到天平的一边,将碎片b和一个1磅的苹果放到天平的另一边,此时天平保持平衡。这说明砝码abc可以称出1磅的质量,我们可以通过交换砝码碎片的组合和摆放位置来称出不同的质量。
如果将这个问题转化为数学符号,其实就是判断方程在W=1,2…40时是否都有解:
x 1 a + x 2 b + x 3 c + x 4 d = W x 1 , x 2 , x 3 , x 4 ∈ { x 1 , x 2 , x 3 , x 4 } x_1a+x_2b+x_3c+x_4d=W\quad x_1,x_2,x_3,x_4\in\left\{x_1,x_2,x_3,x_4\right\} x1a+x2b+x3c+x4d=Wx1,x2,x3,x4{x1,x2,x3,x4}
代码实现如下:

bool isMeasurableOneToForty(int a, int b, int c, int d) {
	int weight;
	for (weight = 1; weight <= 40; weight++) {
		if (!isMeasurable(a, b, c, d, weight)) {
			return false;
		}
	}
	return true;
}
bool isMeasurable(int a, int b, int c, int d, int weight) {
	int x1, x2, x3, x4;
	for (x1 = -1; x1 <= 1; x1++) {
		for (x2 = -1; x2 <= 1; x2++) {
			for (x3 = -1; x3 <= 1; x3++) {
				for (x4 = -1; x4 <= 1; x4++) {
					if (x1 * a + x2 * b + x3 * c + x4 * d == weight)
						return true;
				}
			}
		}
	}
	return false;
}

在函数isMeasurableOneToForty()中,通过一个循环语句来判断砝码碎片组合abcd是否可以称出1~40的任意整数磅。如果可以则返回true,否则返回false
函数isMeasurable()包含5个参数。如果碎片组合abcd能称出weight所表示的质量,则函数isMeasurable()返回true,否则返回false。

#include<iostream>
bool isMeasurable(int a, int b, int c, int d, int weight);
bool isMeasurableOneToForty(int a, int b, int c, int d);
void MeziakWeight();
int main() {
	MeziakWeight();
}
void MeziakWeight() {
	int a, b, c, d;
	for (a = 1; a < 40; a++) {
		for (b = a + 1; b < 40; b++) {
			for (c = b + 1; c < 40; c++) {
				for (d = c + 1; d < 40; d++) {
					if (a + b + c + d == 40 && isMeasurableOneToForty(a, b, c, d)) {
						std::cout << a << " " << b << " " << c << " " << d << std::endl;
					}
				}
			}
		}
	}
}
bool isMeasurableOneToForty(int a, int b, int c, int d) {
	int weight;
	for (weight = 1; weight <= 40; weight++) {
		if (!isMeasurable(a, b, c, d, weight)) {
			return false;
		}
	}
	return true;
}
bool isMeasurable(int a, int b, int c, int d, int weight) {
	int x1, x2, x3, x4;
	for (x1 = -1; x1 <= 1; x1++) {
		for (x2 = -1; x2 <= 1; x2++) {
			for (x3 = -1; x3 <= 1; x3++) {
				for (x4 = -1; x4 <= 1; x4++) {
					if (x1 * a + x2 * b + x3 * c + x4 * d == weight)
						return true;
				}
			}
		}
	}
	return false;
}

总结

牺牲时间换取解的全面性。

上面的三个问题都可以化为对整数解的方程组求解的问题。
求解的过程就是对范围内的所有可能值进行尝试。
尝试的时候需要注意重复解的问题。对于那些不限定顺序的题目,内层循环的计数器起始值可以尝试从+1开始。
起始值从0开始还是从1开始,到哪里结束,要看实际问题,要看我们要遍历的是什么。
在排序算法中,我们如果遍历的是下标,那就是从0开始,循环条件就是<=size-1
如果遍历的是下标,那就是从1开始,循环条件就是<=size
在砝码中,解方程时,我们要对变量可能的系数逐一尝试,因此遍历的范围就是[-1,1]
在穷举的时候,要特别注意是否存在重复、如何处理重复的问题以及我遍历的到底是什么的问题。

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

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

相关文章

【PHP语言】医院安全(不良)事件报告系统源码

一、系统概述&#xff1a; 医院安全&#xff08;不良&#xff09;事件报告系统是一种用于医院管理和监管的工具&#xff0c;旨在帮助医疗机构识别、跟踪、分析和解决各种医疗安全事件&#xff0c;提高医疗质量和患者安全。 医院安全&#xff08;不良&#xff09;事件报告系统采…

Linux:nginx基础搭建(源码包)

安装基础环境 准备一个nginx源码包 yum -y install pcre-devel zlib-devel gcc-c useradd -M -s /sbin/nologin nginx tar xf nginx-1.12.0.tar.gz -C /usr/src/ cd /usr/src/nginx-1.12.0/ ./configure --prefix/usr/local/nginx --usernginx --groupnginx --with-http_st…

【unity】RenderFeature的应用(生成水平面的网格线)

【unity】RenderFeature的应用&#xff08;生成水平面的网格线&#xff09; 在URP里RenderFeature是用于后处理效果上的&#xff0c;也还可以实现一些特殊的效果&#xff0c;比如生成网格线。我们可以使用 CommandBuffer来创建地面网格&#xff0c;这样的话可以通过调整 Comman…

【NOSQL】redis哨兵模式、集群搭建

目录 一、redis高可用一、Redis主从复制1.1主从复制的作用1.2主从复制流程 二、搭建Rdeis主从复制2.1安装redis2.1.1环境准备2.1.2安装redis2.1.3设置环境变量2.1.4定义systemd服务管理脚本 2.2修改 Redis 配置文件&#xff08;Master节点操作2.3修改 Redis 配置文件&#xff0…

【SpringMVC】| 异常处理器、基于全注解配置SpringMVC

目录 异常处理器 1. 基于配置的异常处理 2. 基于注解的异常处理&#xff08;用类代替xml配置&#xff09; 基于全注解配置SpringMVC 1. 创建初始化类&#xff0c;代替web.xml 2. 创建SpringConfig配置类&#xff0c;代替spring的配置文件 3. 创建WebConfig配置类&#…

哈夫曼树的原理及构造方法

目录 1. 什么是哈夫曼树 2. 为什么有哈夫曼树 3. 哈夫曼树的原理 3.1 哈夫曼树的构造方法 3.2 哈夫曼解码 3.3 几种定义 4. 哈夫曼二叉树的特点 5. 关于哈夫曼树的代码 1. 什么是哈夫曼树 哈夫曼树解决的是编码问题&#xff0c;给定N个权值作为N个叶子结点&#xff0c;构…

Springboot tomcat bean 默认作用域 singleton 情况下模拟线程不安全情况 设置多例方式 prototype

目录 写一个控制层的类 验证方法 ​编辑 分别执行如下请求&#xff0c;先执行等待时间久的 日志结果 结论 配置多例模式 配置文件application.properties 类加注解 配置类方式 写一个控制层的类 package com.example.ctr;import lombok.extern.slf4j.Slf4j; import …

【ISE】PlanAhead report_timing报VIOLATED问题

这里写自定义目录标题 背景ISE PR结果PlanAhead结果使用ISE twx使用PlanAhead综合、实现 问题 分析Timer Settings 结论&#xff1f; 背景 最近调试一个老型号FPGA&#xff0c;时序问题分析&#xff0c;方便以后参考。 ISE PR结果 在下图可以看到All Constraints Met&#x…

iOS iPadOS safari 独立Web应用屏幕旋转的时候 onresize window.innerHeight 数值不对。

iOS iPadOS safari 独立Web应用屏幕旋转的时候 onresize window.innerHeight 数值不对 一、问题描述 我有一个日记应用&#xff0c;是可以作为独立 Web 应用运行的那种&#xff0c;但在旋转屏幕的时候获取到的 window.innerHeight 和 window.innerWidth 就不对了&#xff0c;…

【C#】使用System.Data.SqlClient 进行简单批量操作

在实际项目开发中&#xff0c;可能会在定时任务里进行批量添加的操作&#xff0c;或者需要写一些小工具进行批量添加测试。 此篇文章就是使用System.Data.SqlClient 进行简单批量操作。 目录 1、批量插入数据1.1、示例代码1.2、列映射1.3、是否需要映射列 2、批量更新数据3、链…

ZigBee组网-基于协议栈的UART实验(实现收发)(保姆级)

目录 基于协议栈的UART实验 前言 协议栈中的TI自带UART的使用实验 UART配置基本步骤 串口初始化 串口发送 串口接收回显 实验效果 拓展 移植我们自己UART串口 移植配置过程 实验效果 基于协议栈的UART实验 前言 与之前的Zigbee裸机实验不同&#xff0c;我们既可以…

神奇的MATLAB解密工具,让你轻松解密.p文件!

大家都知道&#xff0c;MATLAB是一款功能强大的数学软件&#xff0c;但是在进行代码保护和共享方面&#xff0c;却存在一些困难。这时候&#xff0c;一款优秀的MATLAB解密工具就显得尤为重要。它可以帮助我们解决诸多问题&#xff0c;比如.p文件解密、p文件转m代码等。接下来&a…

mallocstacklogging和MallocStackLoggingNoCompact引起的app文稿数据快速增加

最近由于定位一个iOS16系统适配引起的闪退设置了mallocstacklogging和MallocStackLoggingNoCompact。 配置如下&#xff1a; 在上线前测试&#xff0c;结果发现手机存储空间不足。删除了手机的很多图片后&#xff0c;测试不到两分钟&#xff0c;手机存储空间又不足了。查看app…

Flink运行原理

Apache Flink是什么&#xff1f;对于这个问题&#xff0c;Apache软件基金会官方给出了定义&#xff1a;Flink是一种框架和分布式处理引擎&#xff0c;主要用于对无界和有界数据流进行有状态计算。 本文将从以下几个方面来了解flink运行原理&#xff1a; 【Flink运行时四大组件…

骨传导耳机可以长期佩戴吗,几款佩戴舒适的骨传导耳机清单

骨传导耳机是通过耳朵传声方式&#xff0c;提高了听神经的使用频率&#xff0c;对听觉系统所产生刺激会随之下降。目前骨传导耳机主要应用于运动和娱乐两大领域&#xff0c;尤其是在运动场景中骨传导耳机能够避免传统耳机因佩戴入耳式耳机造成的听力下降问题&#xff0c;更能增…

Python批量将doc转成docx并读取docx的内容

有时候我们需要将doc的文件转成docx的格式&#xff0c;但是如果直接修改文件名后缀的话有时候会没有效果&#xff0c;今天我们利用python批量将doc后缀的word文档转成docx的格式。 也找了很多方法&#xff0c;最终还是找到了就是利用win32com去解决这个问题 很多人在执行这一…

【MySQL】不就是事务

前言 嗨咯&#xff0c;小伙伴们大家好呀&#xff0c;我已经一个星期没有更新了&#xff0c;实在抱歉&#xff01;本期我们要学习MySQL初阶中的最后一课&#xff0c;MySQL数据库中的事务也算是近几年面试必考的问题&#xff0c;所以我们一定要认真学习。 目录 前言 目录 一、事…

学会用智慧轻松的方式过生活

曾经&#xff0c;有位远在黑龙江的女性福主告诉峰民&#xff0c;她说她活不久了。 峰民很是惊讶&#xff0c;不可能吧&#xff0c;你才39岁啊&#xff0c; 她说&#xff1a;我查出了子宫有瘤&#xff0c;峰民听后就说&#xff0c;没事&#xff0c;放心&#xff0c;肯定是良性。…

Python3,处理Excel文件IO流的方法那么多,或许只有Pandas算得上靠谱。

Pandas处理Excel文件IO流的方法 1、引言2、代码实例2.1 什么是文件IO流2.1.1定义2.1.2 字节流、字符流 2.2 常见的Excel文件IO流处理方法2.3 Pandas处理Excel文件IO流2.3.1 直接读取处理2.3.2 转换io流进行处理 3、总结 1、引言 小屌丝&#xff1a;鱼哥&#xff0c; 求助。 小…

深度学习技巧应用22-构建万能数据生成类的技巧,适用于CNN,RNN,GNN模型的调试与训练贯通

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下深度学习技巧应用22-构建万能数据生成类的技巧&#xff0c;适用于CNN,RNN,GNN模型的调试与训练贯通。本文将实现了一个万能数据生成类的编写&#xff0c;并使用PyTorch框架训练CNN、RNN和GNN模型。 目录&#xff1…