我的算法修炼之路--4 ———我和算法的爱恨情仇

文章目录


💗博主介绍:计算机专业的一枚大学生 来自重庆 @燃于AC之乐✌专注于C++技术栈,算法,竞赛领域,技术学习和项目实战✌💗

💗根据博主的学习进度更新(可能不及时)

💗后续更新主要内容:C语言,数据结构,C++、linux(系统编程和网络编程)、MySQL、Redis、QT、Python、Git、爬虫、数据可视化、小程序、AI大模型接入,C++实战项目与学习分享。

👇🏻 精彩专栏 推荐订阅👇🏻

点击进入🌌作者专栏🌌:
算法画解
C++

🌟算法相关题目点击即可进入实操🌟

感兴趣的可以先收藏起来,请多多支持,还有大家有相关问题都可以给我留言咨询,希望希望共同交流心得,一起进步,你我陪伴,学习路上不孤单!

前言:

这些题目摘录于洛谷,好题,典型的题,考察各类算法运用,可用于蓝桥杯及各类算法比赛备战,算法题目练习,提高算法能力,补充知识,提升思维。

锻炼解题思路,从学会算法模板后,会分析,用到具体的题目上。

对应题目点链接即可做。

本期涉及算法:哈希表,前缀和,背包问题,特殊枚举,字符串函数运用与细节处理,贪心算法

题目清单

1.火柴棒等式

题目: P1149 [NOIP 2008 提高组] 火柴棒等式

解法:暴力枚举

n的最大值为24,由于 "+" 和 "=" 要用掉4根火柴,剩下20根火柴。 进行估算, 1111 + 1 = 1112 要用掉25根火柴,所以四位数以上不考虑。711 + 0 = 711,满足条件,所以枚举所有1~1000即可,判断是否刚好等于n。

细节: 因为不能有前导0,要对0特殊处理

代码

cpp 复制代码
#include <iostream>
using namespace std;

int n;
int a[] = {6, 2, 5, 5, 4, 5, 6, 3, 7, 6}; 
int calc(int x)
{
	if(x == 0) return 6; //边界处理 
	int sum = 0;
	while(x)
	{
		sum += a[x % 10];
		x /= 10;
	}
	return sum;
}

int main() 
{
	cin >> n;
	int ret = 0;
	for(int a = 0; a <= 1000; a++)
	{
		for(int b = 0; b <= 1000; b++)
		{
			int c = a + b;
			
			if(calc(a) + calc(b) + calc(c) + 4 == n) 
			ret++;
		}
	}
	
	cout << ret << endl;
	return 0;
}

2.导弹拦截

题目: P1158 [NOIP 2010 普及组] 导弹拦截


解法:特殊枚举 + 排序

模型转化,将导弹,拦截系统位置和拦截区域范围看成点,圆心,和半径。 问题就转化为包含所有的点,求出两个半径的最小平方和。

首先,这里很容易想到错误的贪心策略,就是枚举所有的导弹,将每个导弹分配到近的那个中心区域,这样看似满足最短距离,实则错误,如下:

这道题的枚举策略,思路很特别,需要好好积累。

就是固定一个圆的半径,那么它能包含的点就固定,另外一个原的半径就为圆心到未包含的最远的点的距离。

我们可以现予处理出所有的点到第一个圆心的距离,然后从大到小进行排序,对其进行枚举,相当于第一个圆的区域范围不断变小,至少放出一个点给第二个圆包含,每次进行max,求第二个圆范围,再不断min更新结果,枚举处所有情况的最小值。

注意: 循环到n+1,处理第一个圆半径为0的边界 。


代码:

cpp 复制代码
#include <iostream>
#include <algorithm>
using namespace std;

const int N = 1e5 + 10;
int x1, y1, x2, y2, n;

struct node
{
	int x, y, d;
}a[N];

int calc(int i, int x, int y)
{
	int dx = x - a[i].x;
	int dy = y - a[i].y;
	return dx * dx + dy * dy;
}

bool cmp(node& a, node& b)
{
	return a.d > b.d;
}

int main()
 {
 	cin >> x1 >> y1 >> x2 >> y2;
 	cin >> n;
 	for(int i = 1; i <= n; i++)
 	{
 		cin >> a[i].x >> a[i].y;
 		a[i].d = calc(i, x1, y1);
	 }
	 
	 sort(a + 1, a + 1 + n, cmp);
	 
	 int ret = a[1].d;
	 int r2 = 0;
	 
	 for(int i = 2; i <= n + 1; i++) //循环到n+1,处理第一个圆半径为0的边界 
	{
		int r1 = a[i].d;
		r2 = max(r2, calc(i - 1, x2, y2));
		
		ret = min(ret, r1 + r2);
	}
	
	cout << ret << endl;
	return 0;
}

3.铺设道路

题目: P5019 [NOIP 2018 提高组] 铺设道路

解法:贪心

从前往后遍历对于第i个,分两类情况:

1.a[i - 1] < a[i]: 只需要填上a[i] - a[i - 1], 因为a[i - 1] 的部分会在处理a[i - 1]时填上;

2.a[i - 1] >= a[i]: 不需要填,因为前面填a[i - 1]时会填上。


代码:

cpp 复制代码
#include <iostream>
using namespace std;

const int N = 1e5 + 10;
int n;
int a[N];
 
int main() 
{
	cin >> n;
	int ret = 0;
	
	for(int i = 1; i <= n; i++)
	{
		cin >> a[i];
		if(a[i] > a[i - 1])
		{
			ret += a[i] - a[i - 1];
		}
	}
	
	cout << ret << endl;
	
	return 0;
}

4.Shaass and Bookshelf

题目: CF294B Shaass and Bookshelf



解法:(动态规划)01背包

如果在选择每一本书摆放位置的时候,既考虑上面的宽度,又考虑下面厚度,解决起来非常麻烦。

这里类比"导弹拦截"那道题的的思路。

如果看水平放置的书籍,确定下来之后,下面的书籍的厚度是可以用总厚度sum 减去上面书籍的厚度i直接计算出来。这样,我们只用考虑上面书籍的所有厚度下的最小宽度,然后计算对应下面书籍的厚度是否合法即可。计算上面书籍所有可能的厚度下的最小宽度,正好对应01 背包问题。

1.状态表示:

f[i] [j]表示:从前i 个书本中挑选,水平放置的时候,总厚度恰好为j 时,此时的最小宽度。

注意: 处理结果与以往不同,最终结果就是所有符合sum - j >= f[j] 里面,sum - j的最小值。(j越大,sum - j越小,第一个合法的即为最小值 )

2.状态转移方程:

对于i 位置的书籍,有选或不选两种情况:

a.如果不选:那就是在 [1,i − 1] 区间内,去凑厚度恰好为j时的最小宽度 f[i - 1] [j]

b.如果选上i 位置书籍:那就是在 [i,i − 1] 区间内,去凑厚度恰好为 j - t[i] 时的最小宽度,然后再加上i 位置宽度f[i - 1] [j - t[i] + w[i]

因为要的是最小值,所以取上面两种情况的最小值。其中第二种情况注意判断是否存在。

3.初始化:

可以把f表初始化为正无穷0x3f3f3f3f,然后f[0] [0]初始化为0。

不合法的状态初始化为无穷大,取最小值的时候就不会影响结果;

f[0] [0]是一个合法的位置,同时也为了让后续填表是正确的。

4.填表顺序:

从上往下每一行,每一行从左往右。

空间优化版本:每一行从右往左。(优化一维,逆序

代码:

cpp 复制代码
#include <iostream>
#include <cstring>
using namespace std;

const int N = 110, M = 1e4 + 10;

int n;
int t[N], w[N];
int f[M]; //f[N][M] 优化一维, f[i][j],从前i本书挑选,水平放置厚度为j,此时的最小宽度
 
int main() 
{
	cin >> n;
	int sum = 0;
	for(int i = 1; i <= n; i++)
	{
		cin >> t[i] >> w[i];
		sum += t[i];
	}
	
	memset(f, 0x3f, sizeof f);
	f[0] = 0;
	for(int i = 1; i <= n; i++)
	{
		for(int j = sum; j >= t[i]; j--) //优化一维,逆序 
		{
			f[j] = min(f[j], f[j - t[i]] + w[i]);
		}
	}
	
	for(int j = sum; j >= 0; j--) //j越大,sum - j越小 
	{
		if(f[j] <= sum - j) //第一个合法的即为最小值 
		{
			cout << sum - j << endl;
			break;
		}
	}
	return 0;
}

5.统计单词数

题目: P1308 [NOIP 2011 普及组] 统计单词数

解法:模拟 + 字符串函数t.find(s)

细节处理:1.读入两行字符串,要用getchar()函数 读取处理"\n";2. 第二行有空格 ,要用getline(cin, string) 读取。3.这里对其精妙的处理方法,预处理目标字符串和查找字符串首尾加上' '(空格),这样就不会出现所查找的字符串是文章单词的部分; 4.对find()的运用,t.find(s)是在字符串t中从起始位置查找s字符串出现的第一个位置并返回,可以用下标first存结果(第一个的位置),pos++,找后面的位置,t.find(pos,s)。

代码:

cpp 复制代码
#include <iostream>
using namespace std;
 
int main() 
{
	string s, t;
	cin >> s;
	getchar(); //读入换行符
	getline(cin, t); //要读空格
	
	//预处理
	s = ' ' + s + ' ';
	t = ' ' + t + ' ';
	
	//大写转小写
	for(int i = 0; i < s.size(); i++)
	{
		s[i] = tolower(s[i]);
	 } 
	 
	 for(int i = 0; i < t.size(); i++)
	 {
	 	t[i] = tolower(t[i]);
	 }
	 
	 int cnt = 0, first = 0;
	 if(t.find(s) == -1)
	 {
	 	cout << -1 << endl;
	 }
	 else
	 {
	 	first = t.find(s);
		 int pos = 0;
		 while(t.find(s, pos) != -1)
		 {
		 	pos = t.find(s, pos);
			 cnt++;
			 
			 pos++; 
		  } 
		  
		  cout << cnt << " " << first << endl;
	 }
	return 0;
}

6.连续自然数和

题目: P1147 连续自然数和

解法:前缀和 + 哈希表

对于一个连续的区间求和,首先想到前缀和进行处理,要找一个区间的值为m,那么就起始点到这点i的和sum(前缀和)➖ 前面某点j的区间和为sum - m即为m区间,注意: 对应m区间下标为 [j + 1, i] 。而要想快速知道前缀和对应的下标,可以用经典的套路 前缀和 + 哈希表 ,mp[sum] = i, 即为前缀和为sum对应的索引。

代码:

cpp 复制代码
#include <iostream>
#include <unordered_map>
using namespace std;

typedef long long LL;

int m;
unordered_map<LL, int> mp; //前缀和对应的索引

int main() 
{
	cin >> m;
	int n = (m + 1) / 2; //枚举范围
	mp[0] = 0; //<0, 0> 赋值语句(执行操作)不能直接在全局作用域执行 
	LL sum = 0;
	for(int i = 1; i <= n; i++)
	{
		sum += i;
		
		//sum - m
		if(mp.count(sum - m))
		{
			cout << mp[sum - m] + 1 << " " << i << endl; 
		}
		mp[sum] = i;
	}
	return 0;
}


看到这里请点个赞,关注,如果觉得有用就收藏一下吧。后续还会持续更新的。 创作不易,还请多多支持!

相关推荐
MM_MS8 小时前
Halcon变量控制类型、数据类型转换、字符串格式化、元组操作
开发语言·人工智能·深度学习·算法·目标检测·计算机视觉·视觉检测
独自破碎E9 小时前
【二分法】寻找峰值
算法
mit6.8249 小时前
位运算|拆分贪心
算法
ghie90909 小时前
基于MATLAB的TLBO算法优化实现与改进
开发语言·算法·matlab
恋爱绝缘体19 小时前
2020重学C++重构你的C++知识体系
java·开发语言·c++·算法·junit
wuk9989 小时前
VSC优化算法MATLAB实现
开发语言·算法·matlab
Z1Jxxx10 小时前
加密算法加密算法
开发语言·c++·算法
乌萨奇也要立志学C++10 小时前
【洛谷】递归初阶 三道经典递归算法题(汉诺塔 / 占卜 DIY/FBI 树)详解
数据结构·c++·算法
vyuvyucd10 小时前
C++引用:高效编程的别名利器
算法