当前位置: 首页 > article >正文

【C++ 算法进阶】算法提升七

目录

  • 正数数组中那两个数&结果最大 (贪心)
    • 题目
    • 题目分析
    • 代码详解
  • 最小相机覆盖问题 (二叉树递归套路)
    • 题目
    • 题目分析
    • 代码详解
  • 拼接字符串 (动态规划 + 前缀树)
    • 题目
    • 题目分析
    • 代码详解

正数数组中那两个数&结果最大 (贪心)

题目

给定一个由正整数组成的数组 现在要求你返回哪两个数组 & 的结果最大

题目分析

这道题和算法提升六中的异或有着明显的不同 因为在&运算中 只要有一个数是0 最后的结果就是0

也就是说 如果我们当前位是0 走0号线 1号线都是有理由的 但是我们无法比较0和1号线下面谁更好 所以说前缀树的方式是行不通的

但是 & 的特性告诉我们 尽量要前面位数的数字都为1

所以说我们就能有这样的思路

从最高位开始遍历整个数组 查看当前位置1的数量

  • 如果数量小于1 则说明当前位必定是0 没法求出最大值
  • 如果数量等于2 我们就可以直接将这两个数相与 得出最终答案
  • 如果数量大于3 则说明当前位必定是1 继续求下个为止的数据即可

代码详解

int Maxxx(vector<int>& arr) {
	// lnSize 数组的大小
	int ans = 0;
	int M = arr.size();
	// move 移动的位数
	for (int move = 30; move >= 0; move--) {
		// lnBit 当前比特位的数字
		int tmp = M;
		int lnBit = 0; 
		int count = 0;
		int i = 0;
		while (i < M)
		{
			lnBit = (arr[i] >> move) & 1;
			if (lnBit == 1) {
				int tmp = arr[i];
				arr[i] = arr[count];
				arr[count] = tmp;
				count++;
			}
			i++;
		}

		M = count;

		if (count < 2) {
			M = tmp;
		}

		if (count == 2) {
			return arr[0] & arr[1];
		}
	}

	return arr[0] & arr[1];
}

最小相机覆盖问题 (二叉树递归套路)

题目

给定一个二叉树 我们在树的节点上安装摄像头

节点上的每个摄影头都可以监视其父对象 自身及其子节点

计算监控树的所有节点所需的最小摄像头数量

题目分析

如果我们这里按照一般的二叉树套路分析 这里有这两种情况

  • 当前节有相机 且下方节点都被覆盖
  • 当前节点无相机 且下方节点都被覆盖

但是只是这两种可能性 我们不能得出最佳答案

在这里插入图片描述
我们假设当前节点为X 如果只根据情况1 2 我们得不出最佳的答案

最佳答案为在X的子节点放置一个相机

所以说我们要推出这三种可能性

  1. X有相机 且X下方都被覆盖了
  2. X无相机 且X下方都被覆盖了 且X也被覆盖了
  3. X无相机 且X下方都被覆盖了 且X没被覆盖

代码详解

#include <iostream>
#include <memory>

class TreeNode {
public:
    int val;
    TreeNode* left;
    TreeNode* right;

    explicit TreeNode(int value) : val(value), left(nullptr), right(nullptr) {}
};

enum class Status {
    Uncovered,
    CoveredNoCamera,
    CoveredHasCamera
};

class Data {
public:
    Status status;
    int camera;

    Data(Status stat, int cam) : status(stat), camera(cam) {}
};

// 独立的 process 函数
Data process(TreeNode* root) {
    if (!root) return Data(Status::CoveredHasCamera, 0);
    
    Data left = process(root->left);
    Data right = process(root->right);
    int camera = left.camera + right.camera;

    if (left.status == Status::CoveredNoCamera || right.status == Status::CoveredNoCamera) {
        return Data(Status::CoveredHasCamera, camera + 1);
    }
    if (left.status == Status::CoveredHasCamera || right.status == Status::CoveredHasCamera) {
        return Data(Status::CoveredHasCamera, camera);
    }
    return Data(Status::Uncovered, camera);
}

class CameraCoverage {
public:
    static int resolveMinCamera(TreeNode* root) {
        Data data = process(root);
        return data.camera + (data.status == Status::Uncovered ? 1 : 0);
    }
};

int main() {
    // 测试用例可以在这里添加
    return 0;
}

拼接字符串 (动态规划 + 前缀树)

题目

假设所有字符都是小写字母 字符串为str

arr是去重的单词表 每个单词都不是空字符且可以使用N次

使用arr中的字符有多少种可以拼接str的方法 (不存在无解的情况)

题目分析

这道题很明显是一个从左到右的动态规划模型

我们根据这个模型可以设置如下的递归函数

int process(string& s, int i) 

我们规定这个函数的意义是 返回从string的i位置开始 返回有多少种方法数可以让单词拼接成s

我们假设所有的单词都被我们放在了set里

代码详解

#include <unordered_set>
#include <vector>
#include <string>

using namespace std;

unordered_set<string> wordSet; // 假设已经填充好单词表
vector<int> dp;

int process(const string& s, int i) {
    if (i == s.size()) {
        return 1;
    }
    
    if (dp[i] != -1) {
        return dp[i]; // 如果已经计算过,直接返回结果
    }

    int lnWays = 0;
    for (int index = i + 1; index <= s.size(); index++) {
        // 直接检查当前子字符串 s[i:index] 是否在 wordSet 中
        if (wordSet.count(s.substr(i, index - i))) {
            lnWays += process(s, index); // 递归调用下一个位置
        }
    }

    return dp[i] = lnWays; // 存储结果
}

int countWays(const string& str) {
    dp.assign(str.size(), -1); // 初始化动态规划数组
    return process(str, 0);
}

我们这里就写出来了动态规划无优化的方法

但是这种方法的时间复杂度显然是过高的 因为每次拼接字符串的时候我们都需要遍历整个数组 导致了很多无用的遍历 (我们只需要遍历set中有的就行了)

那么这个时候我们就想起用前缀树了

class TireNode
{
public:
	unordered_map<char, TireNode*> children;
	bool isEnd = false;
};

class Tire
{
public:
	TireNode* root;

	Tire() {
		root = new TireNode();
	}


	void insert(string& str) {
		TireNode* cur = root;
		for (auto ch : str) {
			if (!cur->children.count(ch)) {
				cur->children[ch] = new TireNode();
			}
			cur = cur->children[ch];
		}
		cur->isEnd = true;
	}

};

这个时候我们只需要用一个map来记录下各个单词可能走的路径 并且继续下各个单词可能的节点 就能极大的优化查找字符串的效率

unordered_set<string> set;
int process(string& s, int i) {
	if (i == s.size())
	{
		return 1;
	}

	int ways = 0;
	TireNode* node = tire.root;
	for (int index = i; index < s.size(); index++)
	{
		char ch = s[index];
		if (node ->children.count(ch)) {
			node = node->children[ch];
			if (node->isEnd == true) {
				ways += process(s, index + 1);
			}
		}
		else
		{
			break;
		}
	}

	return ways;
}

http://www.kler.cn/a/376051.html

相关文章:

  • 无人机螺旋桨动平衡分析测试台
  • VScode通过ssh连接服务器(使用私钥时的易忽视点)
  • 完全透彻了解一个asp.net core MVC项目模板1
  • 代码随想录算法训练营第三十三天|Day33 动态规划
  • 逻辑磁盘管理 附实验:逻辑卷的组成与划分
  • 闪存学习_1:Flash-Aware Computing from Jihong Kim
  • mysql 的内连接、左连接、右连接有什么区别?
  • 阿里云服务器 篇九:个人博客类网站
  • Node.js——fs模块-文件流式写入
  • 从头开始学PHP之面向对象
  • 【多模态RAG】多模态RAG ColPali实践
  • Unity WebGL项目中,如果想在网页端配置数字人穿红色上衣,并让桌面端保持同步
  • 3.使用ref定义页面元素,
  • ZooKeeper 客户端API操作
  • 工厂电气及PLC【1章各种元件符号】
  • T-Mobile股票分析:T-Mobile的股价还能继续上涨吗?
  • 动态ip如何自动更换ip
  • Apache Paimon主键表的一些最佳实践
  • 3d点在立方体内(numpy,不使用for循环)
  • 免费送源码:Java+Springboot+MySQL Springboot酒店客房管理系统的设计与实现 计算机毕业设计原创定制
  • [Python技术]利用akshare获取股票基本信息、K线图、最新新闻 以及大模型投资建议
  • 电脑换网络环境,IP地址会变吗?答案来了
  • 1008:计算(a+b)/c的值
  • 使用 ADB 在某个特定时间点点击 Android 设备上的某个按钮
  • 我的工具列表
  • DCN网络进行新冠肺炎影像分类