Leetcode学习之深度优先搜索与宽度优先搜索(2)

开宗明义:本系列基于小象学院林沐老师课程《面试算法 LeetCode 刷题班》,刷题小白,旨在理解和交流,重在记录,望各位大牛指点!


Leetcode学习之深度优先搜索与宽度优先搜索(2)



1、火柴棍摆正方形 LeetCode 200.

题目来源 L e e t C o d e   473.   M a t c h s t i c k s   t o   S q u a r e LeetCode \ 473. \ Matchsticks \ to \ Square LeetCode 473. Matchsticks to Square
描述已知一个数组,保存了n个火柴棍(n<=15),问可否使用这n个火柴棍摆成1个正方形(这边是n个全农)
在这里插入图片描述
思考
在这里插入图片描述


1.1 无优化的深度搜索

在这里插入图片描述明显时间成本太高,pass!


1.2 优化与剪枝

分析对上述暴力搜索添加约束就是优化
在这里插入图片描述

测试代码:

#include <vector>
#include <stdio.h>
#include <algorithm>
using namespace std;

class Solution {
public:
	bool makesquare(vector<int>& nums) {
		if (nums.size() < 4) {	//数量小于4的时候返回假
			return false;
		}

		int sum = 0;
		for (int i = 0; i < nums.size(); i++) {
			sum = sum + nums[i];
		}

		if (sum % 4) {	//n个火柴杆的总和对4取余为0,否则返回假  
			return false;
		}

		std::sort(nums.rbegin(), nums.rend());	//排序
		int bucket[4] = { 0 };	//vector<int>
		return generate(0, nums, sum / 4, bucket);
	}
private:
	bool generate(int i, vector<int>& nums, int target, int bucket[]) {	//target 1/4的sum和
		if (i >= nums.size()) {
			return bucket[0] == target&&bucket[1] == target&&bucket[2] == target
				&&bucket[3] == target;
		}
		for (int j = 0; j < 4; j++) {
			if (bucket[j] + nums[i] > target) {		//4个桶中分别尝试
				continue;
			}
			bucket[j] += nums[i];	//放在j桶中 <=

			if (generate(i + 1, nums, target, bucket)) {
				return true;
			}
			bucket[j] = bucket[j] - nums[i];
		}
		return false;
	}
};

int main() {
	vector<int> nums;
	nums.push_back(1);
	nums.push_back(1);
	nums.push_back(2);
	nums.push_back(4);
	nums.push_back(3);
	nums.push_back(2);
	nums.push_back(3);
	Solution solve;
	printf("%d\n", solve.makesquare(nums));

	system("pause");
	return 0;
}

效果图
在这里插入图片描述


1.3 位运算法

描述

  • 使用位运算法,构造出所有和为 t a r g e t target target (总和/4)的子集,存储在向量 o k − s u b s e t ok-subset oksubset 中,这些是候选的边组合
  • 遍历所有的 o k − s u b s e t ok-subset oksubset,两两进行对比,如果 o k − s e t [ i ] ok-set[i] okset[i] o k − s e t [ j ] ok-set[j] okset[j] 进行与运算的结果为0,则说明 o k − s e t [ i ] ok-set[i] okset[i] o k − s e t [ j ] ok-set[j] okset[j] 表示的是无交集的两个集合(没有选择同样的火柴棍),这两个集合可以代表两个可以同时存在的满足的条件的边;将 o k − s e t [ i ] ok-set[i] okset[i] o k − s e t [ j ] ok-set[j] okset[j] 求或,结果存储在 o k − h a l f ok-half okhalf 中,它代表所有满足一半结果的情况
  • 遍历所有的 o k − h a l f ok-half okhalf ,两两进行对比,如果 o k − h a l f [ i ] ok-half[i] okhalf[i] o k − h a l f [ j ] ok-half[j] okhalf[j] 进行与运算的结果为0,则返回true(说明有4个满足条件的边,即可组成正方形);否则返回 f a l s e false false
    在这里插入图片描述
    测试代码:
#include <vector>
#include <stdio.h>
#include <algorithm>
using namespace std;

class Solution {
public:
	bool makesquare(vector<int>& nums) {
		if (nums.size() < 4) {	//nums.size()<4 返回假
			return false;
		}
		int sum = 0;
		for (int i = 0; i < nums.size(); i++) {
			sum = sum + nums[i];
		}
		if (sum % 4) {	//总和不是4的倍数,肯定不行
			return false;
		}

		int target = sum / 4;	//目标为:总和的四分之一
		vector<int> ok_subset;	//所有满足条件的一个边代表的集合,这边很重要
		vector<int> ok_half;	//所有满足条件的两个边代表的集合。同样很重要

		int all = 1 << nums.size();  //左移一位,就是乘以2
		//构造出所有和为 sum/4 的子集,存储在sub_set中
		for (int i = 0; i < all; i++) {
			int sum = 0;
			for (int j = 0; j < nums.size(); j++) {
				if (i&(1 << j)) {
					sum = sum + nums[j];
				}
			}
			if (sum == target) {
				ok_subset.push_back(i);
			}
		}

		for (int i = 0; i < ok_subset.size(); i++) {
			for (int j = i + 1; j < ok_subset.size(); j++) {
				if ((ok_subset[i] & ok_subset[j]) == 0) {
					ok_half.push_back(ok_subset[i] | ok_subset[j]);
				}
			}
		}
		for (int i = 0; i < ok_half.size(); i++) {
			for (int j = i + 1; j < ok_half.size(); j++) {
				if ((ok_half[i] & ok_half[j]) == 0) {
					return true;
				}
			}
		}
		return false;
	}
};

int main() {
	vector<int> nums;
	nums.push_back(1);
	nums.push_back(1);
	nums.push_back(2);
	nums.push_back(4);
	nums.push_back(3);
	nums.push_back(2);
	nums.push_back(3);
	Solution solve;
	printf("%d\n", solve.makesquare(nums));

	system("pause");
	return 0;
}

在这里插入图片描述


2、收集雨水2 LeetCode 407.

题目来源 L e e t C o d e   407.   T r a p p i n g   R a i n   W a t e r   I I LeetCode \ 407. \ Trapping \ Rain \ Water \ II LeetCode 407. Trapping Rain Water II
描述已知一个 m ∗ n m*n mn 的二维数组,数组存储正整数,代表一个个单元的高度(立方体),将这些立方体想象成水槽,问如果下雨这些立方体中会有多少积水
在这里插入图片描述
分析
在这里插入图片描述
在这里插入图片描述
算法思路
在这里插入图片描述
具体分析
在这里插入图片描述
在这里插入图片描述
测试代码:

#include <stdio.h>
#include <queue>
using namespace std;

struct Qitem {
	int x;
	int y;
	int h;
	Qitem(int x, int y, int h) :x(x), y(y), h(h) {}
};

struct cmp {
	bool operator()(const Qitem &a, const Qitem &b) {
		return a.h > b.h;
	}
};

class Solution {
public:
	int trapRainWater(vector<vector<int>>& heightMap) {  //这个heightMap 是二维数组
		priority_queue<Qitem, vector<Qitem>, cmp> Q;	//设置优先级队列Q,最小堆

		if (heightMap.size() < 3 || heightMap[0].size() < 3) {
			return 0;	//行数或列数小于3,无法积水
		}

		int row = heightMap.size();		//行
		int column = heightMap[0].size();	//列

		vector<vector<int>> mark;	//标记数组mark

		for (int i = 0; i < row; i++) {
			mark.push_back(vector<int>());
			for (int j = 0; j < column; j++) {
				mark[i].push_back(0);
			}
		}  //将二维标记数组都置0

		for (int i = 0; i < row; i++) {
			Q.push(Qitem(i, 0, heightMap[i][0]));
			mark[i][0] = 1;
			Q.push(Qitem(i, column - 1, heightMap[i][column - 1]));
			mark[i][column - 1] = 1;
		}

		for (int i = 1; i < column - 1; i++) {  //列
			Q.push(Qitem(0, i, heightMap[0][i]));	//将四周的点添加至优先级队列Q,并做标记mark。这个添加优先级队列里面自己会处理
			mark[0][i] = 1;
			Q.push(Qitem(row - 1, i, heightMap[row - 1][i]));
			mark[row - 1][i] = 1;
		}	//最上面一行和最下面一行

		static const int dx[] = { -1,1,0,0 };
		static const int dy[] = { 0,0,-1,1 };	//方向数组


		int result = 0;	//最终积水量
		while (!Q.empty()){	//搜索的点
			int x = Q.top().x;	//取队列头部信息
			int y = Q.top().y;
			int h = Q.top().h;
			Q.pop();

			for (int i = 0; i < 4; i++) {
				int newx = x + dx[i];
				int newy = y + dy[i];

				if (newx < 0 || newx >= row ||
					newy < 0 || newy >= column || mark[newx][newy]) {
					continue;	
				}
				if (h > heightMap[newx][newy]) {	//当新拓展的点超出边界或者已加入队列
					result = result + h - heightMap[newx][newy];
					heightMap[newx][newy] = h;
				}

				Q.push(Qitem(newx, newy, heightMap[newx][newy]));
				mark[newx][newy] = 1;	//标记已经搜索好了
			}
		}
		return result;
	}
};

int main() {
	int test[][10] = {
		{1,4,3,1,3,2},
		{3,2,1,3,2,4},
		{2,3,3,2,3,1}
	};

	vector<vector<int>> heightMap;
	for (int i = 0; i < 3; i++) {
		heightMap.push_back(vector<int>());
		for (int j = 0; j < 6; j++) {
			heightMap[i].push_back(test[i][j]);
		}
	}
	Solution solve;
	printf("%d\n", solve.trapRainWater(heightMap));
	system("pause");
	return 0;
}

效果图
在这里插入图片描述


2.1 结构体的STL优先级队列

注意:里面最小堆的用法
测试代码:

#include <stdio.h>
#include <queue>
using namespace std;

struct Qitem {
	int x;
	int y;
	int h;
	Qitem(int x, int y, int h) :x(x), y(y), h(h) {}
};

//判断两个值的高度哪个大
struct cmp {
	bool operator()(const Qitem &a, const Qitem &b) {
		return a.h > b.h;
	}
};

int main() {
	priority_queue<Qitem, vector<Qitem>, cmp>	Q;  //优先输出小数据。这边注意
	Q.push(Qitem(0, 0, 5));		//导入元素
	Q.push(Qitem(1, 3, 2));
	Q.push(Qitem(5, 2, 4));
	Q.push(Qitem(0, 1, 8));
	Q.push(Qitem(6, 7, 1));
	while (!Q.empty()) {
		int x = Q.top().x;
		int y = Q.top().y;
		int h = Q.top().h;
		printf("x = %d, y = %d, h = %d\n", x, y, h);
		Q.pop();
	}
	system("pause");
	return 0;
}

效果图
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值