2-4基础算法-离散化 贪心 01背包问题,一名毕业三年的女程序媛面试头条经验

25 阅读5分钟

最后前端到底应该怎么学才好?

如果你打算靠自己摸索自学,那么你首先要了解学习前端的基本大纲,这是你将要学习的主要内容,理解以及掌握好这些内容,便可以找到一份初级的前端开发工作。你还需要有一套完整的前端学习教程,作为初学者最好的方式就是看视频教程学习,初学者容易理解接受。

不要选择买书学习,这样的方式没有几个人能学会,基本都是看不下去书,也看不懂书。如果喜欢看书的学弟,可以买一些经典的书籍作为辅助即可,主要还是以看教程为主。每天抽出固定几个小时学习,做好长期学习的准备。学习编程并不是每天光看视频,你学习编程最重要的目的是为了编写软件产品,提供给大众使用,所以用手写出代码实现功能才是我们要做的事情。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述 开源分享:docs.qq.com/doc/DSmRnRG…

文章目录

一.离散化

离散化是一种将数组的值域压缩,从而更加关注元素的大小关系的算法。
离散化数组要求内部有序(一般去重)
可以通过离散化下标得到值
也可以通过值得到离散化下标
在这里插入图片描述

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
	int a[6] = { 0,3,1000,2,99999,2 };//原数组
	vector<int> L;
	for (int i = 1; i < 6; i++) {
		L.push\_back(a[i]);
	}
	sort(L.begin(), L.end());
	auto b=unique(L.begin(), L.end());
	L.erase(b, L.end());
	
	for (int i = 0; i < L.size(); i++) {
		cout << L[i]<<" ";  //2 3 1000 99999
	}
	//获取1000在L中的下标.lower\_bound返回从左到右第一个大于等于1000 的地址
	cout << lower\_bound(L.begin(), L.end(), 1000) - L.begin();  //2

二.贪心

1.最小化战斗力差距
在这里插入图片描述
在这里插入图片描述
评测系统

分析:先进行排序,从某个位置划分,左侧是a,右侧是b。则max a和min b是相邻的。只需计算相邻两数的差最小值

#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
const int N = 1e5 + 5;
int main()
{
	int n;
	cin >> n;
	int a[N] = { 0 };
	int minnum = INT_MAX;
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}
	sort(a, a + n);
	for (int i = 1; i < n; i++) {
		minnum = min(minnum, a[i] - a[i - 1]);
	}
	cout << minnum;
}

2.谈判
在这里插入图片描述
在这里插入图片描述
评测系统

分析:贪心:每次选择最小的两个部落合并

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e3 + 5;
int main()
{
	int n;
	cin >> n;
	int a[N] = { 0 };
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}
	sort(a, a + n);
	int sum = a[0];
	int sum2 = 0;
	int k = 1;
	while (k<n) {
		sum = sum + a[k++];
		sum2+=sum;
	}
	cout << sum2;
}

或优先队列

#include <iostream>
#include <vector>
#include <queue>
using namespace std;
int main()
{
	int n;
	cin >> n;
	priority_queue<int, vector<int>, greater<int>> queue;//小根堆,最小元素放到前面
	for (int i = 0; i < n; ++i) {
		int x;
		cin >> x;
		queue.push(x);
	}
	int sum = 0;
	while (queue.size() > 1) {
		int x = queue.top();
		queue.pop();
		int y = queue.top();
		queue.pop();
		queue.push(x + y);
		sum += x + y;
	}
	cout << sum;
}

3.纪念品分组
在这里插入图片描述
在这里插入图片描述

评测系统

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
	int w, n;
	cin >> w >> n;
	vector<int> a(n);
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}
	sort(a.begin(), a.end());
	int i = 0, j = n - 1;
	int sum = 0;
	while (i <= j) {
		if (a[i] + a[j] <= w) {
			i++;
			j--;
		}
		else {
			j--;
		}
		sum++;
	}
	cout << sum;
}

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
	int w, n;
	cin >> w >> n;
	vector<int> a(n);
	for (int i = 0; i < n; i++) {
		cin >> a[i];
	}
	sort(a.begin(), a.end());
	int i = 0, j = n - 1;
	int sum = n;
	while (i < j) { //注意:这里不能写成i!=j,因为当ij相邻时,若满足if,ij交换,仍可以继续进行,直到越界
		if (a[i] + a[j] <= w) {
			i++;
			j--;
			sum--;
		}
		else {
			j--;
		}
	}
	cout << sum;
}

4.分糖果
在这里插入图片描述
解读:
①字典序:apple排在banana前面,我们认为apple的字典序更小
②假设我们有6个糖果分给3个学生,可能的分法是:ab、cd、ef,则拿到ef的同学字典序最大
③使得字典序最大的字符串尽可能小:指的是字典序最大的字符串的字典序最小,而不是长度最短(除非只有一个种类的糖果)。一种可能的分法中,字典序最大的是abccd;另一种分法中,字典序最大的是d。我们会选择abccd这种分法作为最终结果

分析:
糖果的种类数可能有三种情况
①只有一个种类的糖果:应使分的字典序最大的(分的糖果数最多的)同学拿到的糖果尽可能少(字符串尽可能小)。也就是尽可能的均分。如我们有7个糖果,分为3个同学。应该是2/2/3的分法
②一共有n个糖果,分给x个同学。我们先将输入序列s排序,现在同种类的糖放在了一起。若第x个糖果的种类和第1个糖果的种类不一样。 我们将前x个糖果依次分给x个同学,则第x个同学拿到了字典序最大的,又由于序列是有序的,x对应的字典序一定是当前最小的。从x+1到n(若有)的糖果我们将其分给第一位同学,因为第一位同学的首字母一定小于第x位同学,所以该操作不会产生影响。
③一共有n个糖果,分给x个同学。我们先将输入序列s排序,现在同种类的糖放在了一起。**若第x个糖果的种类和第1个糖果的种类一样。**这时,第x个糖果的字典序一定是最小的(因为已经排序),我们直接输出[x,n]即为最终序列。如:aaaabbbbcccc,4人,显然最佳分配a/a/a/abbbbcccc>其他分配abbb/a/a/accc

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
	int n, x;
	cin >> n >> x;
	vector<char> a(n + 1);
	for (int i = 1; i <= n; i++) {
		cin >> a[i];
	}
	sort(a.begin(), a.end());
	if (a[n] == a[1]) {
		if (n % x == 0) {
			int temp = n / x;
			while (temp--) {
				cout << a[1];
			}
		}
		else {
			int temp = n / x + 1;
			while (temp--) {
				cout << a[1];
			}
		}
	}
	else if (a[x] != a[1]) {
		cout << a[x];
	}
	else {
		for (int i = x; i <= n; i++) {
			cout << a[i];
		}
	}
	return 0;
}

5.最大的卡牌价值
在这里插入图片描述

评测系统

注:最多进行k次,并非一定要进行k次

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
const int N = 1e5 + 5;
struct pro {
	int a;
	int b;
};
bool cmp(const pro& x, const pro& y) {
	return x.b - x.a > y.b - y.a;
}
int main()
{
	int n, k;
	pro arr[N];
	cin >> n >> k;
	long long sum = 0;
	for (int i = 0; i < n; i++) {
		cin >> arr[i].a;
		sum += arr[i].a;
	}
	for (int i = 0; i < n; i++) {
		cin >> arr[i].b;
	}
	sort(arr, arr + n, cmp);
	for (int i = 0; i < n; i++) {
		if (k == 0) {
			break;
		}
		if (arr[i].b > arr[i].a) {
			sum -= arr[i].a;
			sum += arr[i].b;
			k--;
		}
	}
	cout << sum;
}
//也可以定义三个数组运算

6.珠宝的最大交替和
在这里插入图片描述
在这里插入图片描述
评测系统

分析:先求和,然后奇数位找最小的,偶数位找最大的,交换。

#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
using namespace std;
int main()
{
	int n;
	cin >> n;
	vector<int> a(n);
	long long sum = 0;
	int ji=INT_MAX, ou=INT_MIN;
	for (int i = 0; i < n; i++) {
		cin >> a[i];
		if ( i % 2 == 0) {
			sum += abs(a[i]);
			ji = min(ji, abs(a[i]));
		}
		else {
			sum -= abs(a[i]);
			ou = max(ou, abs(a[i]));
		}
	}
	if(n>1&&ji<ou)
		sum = sum - 2 \* ji + 2 \* ou;
	cout << sum;
}

7.小蓝的礼物
在这里插入图片描述
在这里插入图片描述
评测系统

#include <iostream>
#include <vector>
#include <algorithm>
#include <climits>
#include <queue>
using namespace std;
int main()
{
	priority_queue<int, vector<int>, greater<int>> pq;
	int n, k;
	cin >> n >> k;
	for (int i = 0; i < n; i++) {
		int x;
		cin >> x;
		pq.push(x);
	}
	long long sum = 0;
	int num = 0;
	while (pq.size() > 0) {
		int x = pq.top();
		sum += x;
		num++;
		if (sum > k) {
			sum -= x;
			if (x % 2 == 0) {
				if (sum + x/2 > k) {
					num--;
				}
			}
			else{
				if (sum + x/2+1 > k) {
					num--;
				}
			}
			cout << num;
			return 0;
		}
		pq.pop();
	}
	cout << num;
}

8.四个瓷瓶的神秘游戏

在这里插入图片描述

评测系统

分析:当一个瓶子为空的时候,我们依然可以继续操作。当两个瓶子都为空的时候,我们无法操作。

在通过sort排序后,满足a[0]<=a[1]<=a[2]<=a[3]。我们先通过操作将a[0]变为空,可操作次数为a[0]内的珍珠数。此时最后一个瓶内珍珠数最多,为a[3]+2*a[0]

在此基础上,对其他瓶进行处理
因为a[2]不小于a[1],所以能对a[1]进行的操作都可以对a[2]进行操作,我们假设a[2]和a[3]足够大。我们分别假设在a[0]为0时,a[1]分别取1~9,于是有:

在这里插入图片描述

以此类推,我们可以总结出

在这里插入图片描述

可以看出,相差模除为0时,a[1]和a[0]的差值就是a[3]要增加的值;差值模除为其他时,a[3]要增加的值为a[1]和a[0]的差值-1

于是有

#include <iostream>
#include <algorithm>
using namespace std;
int main() { 
    long long x; 
    long long a[4];
    for (int i = 0; i < 4; i++) {
        cin >> a[i];
    }
    sort(a, a + 4); 
    if ((a[1] - a[0]) % 3 == 0)
        cout << a[3] + (a[0] \* 2) + a[1] - a[0];
    else
        cout << a[3] + (a[0] \* 2) + a[1] - a[0]-1;
}

下面考虑a[3]或a[2]非足够大的情况,拥有最多珍珠的瓶子可能就不是a[3]:
在将a[0]变为0的基础上:
当瓶子内珍珠数为0111时,一次操作变为2000,此时瓶内最多珍珠数量为2,但按上述分析前两个瓶01时,a[3]数量不变,也就是1,和2差1
当瓶子内珍珠数为0112时,一次操作变为2001,此时瓶内最多珍珠数2,a[3]数量不变也正好是2,满足上述推理
当瓶子内珍珠数为0113时,一次操作变为2002,此时a[0]=2,a[3]数量不变为3,此时a[3]成为珍珠最多的瓶
当瓶子内珍珠数为0222时,一次操作变为4000,此时瓶内最多珍珠数4,但按上述分析前两个瓶02时,a[3]数量+1,也就是3,和4差1
以此类推
可以看出,当a[3]不是足够大时,或者说,a[3]如果不满足至少比a[2]或a[1]大1时,即a[1]、a[2]、a[3]相等时,a[0]反而成了珍珠最多的瓶
我们据此更新代码,考虑a[1]、a[2]、a[3]相等的情况,要在输出基础上+1

#include <iostream>
#include <algorithm>
using namespace std;
int main() {
	long long a[4];
	for (int i = 0; i < 4; i++) {
		cin >> a[i];
	}
	sort(a, a + 4);

	int x = 0;
	if (a[3] == a[1]) {
		x = 1;
	}
	
	if ((a[1] - a[0]) % 3 == 0)
		cout << a[3] + (a[0] \* 2) + a[1] - a[0]+x;
	else
		cout << a[3] + (a[0] \* 2) + a[1] - a[0] - 1+x;
}

9.鸡哥的购物挑战
在这里插入图片描述
评测系统

分析:商品价格有正有负,计算过程中可抵消

#include <iostream>
#include <algorithm>
#include <queue>
using namespace std;
int main() {
	priority_queue<int> pq;//大根堆
	int n;
	cin >> n;
	for (int i = 0; i < n; i++) {
		int x;
		cin >> x;
		pq.push(x);
	}
	long long sum = 0;
	int num = 0;
	int laste;
	while (pq.size() > 0&&pq.top()>0) {
		sum += pq.top();
		laste = pq.top();//记录while最后一个弹出的元素
		pq.pop();
		num++;
	}
	if (num < n&& num % 2 != 0) {
		if (abs(pq.top()) > laste) {
			sum -= laste;
		}
		else {
			sum += pq.top();
		}
	}
	else if (num == n && num % 2 != 0) {
		sum -= laste;
	}
	cout << sum;
}

10.冒险者公会
在这里插入图片描述
在这里插入图片描述

评测系统

分析:样例说明有一定的误导性,为了更高效的派出冒险者,应该从小到大排序

#include <iostream>
#include <algorithm>
#include <vector>
using namespace std;
const int N = 1e3 + 5;
int cmp(const int a, const int b) {
	return a > b;
}
int main() {
	int m, n;
	cin >> m >> n;
	int x[N] = { 0 }, b[N][N] = { 0 };
	for (int i = 0; i < m; i++) {
		cin >> x[i];
	}
	sort(x, x + m,cmp);
	int maxnum = -1;
	for (int i = 0; i < n; i++) {
		int k;
		cin >> k;
		for (int j = 0; j < k; j++) {
			cin >> b[i][j];
		}
		sort(b[i], b[i] + k,cmp);
		maxnum = max(maxnum, k);
	}
	if (m < maxnum) {


### 刷面试题

刷题的重要性,不用多说。对于应届生或工作年限不长的人来说,刷面试题一方面能够尽可能地快速自己对某个技术点的理解,另一方面在面试时,有一定几率被问到相同或相似题,另外或多或少也能够为自己面试增加一些自信心,可见适当的刷题是很有必要的。

**[开源分享:【大厂前端面试题解析+核心总结学习笔记+真实项目实战+最新讲解视频】](https://docs.qq.com/doc/DSmRnRGxvUkxTREhO)**

* **前端字节跳动真题解析**  

  ![](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/f29caa7af9b449b4bf36964dba980a91~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg55So5oi3MzM5MTQ5MjgwNjA=:q75.awebp?rk3s=f64ab15b&x-expires=1772527783&x-signature=ZXnhLrCTfn8vXTNQtrwKDAwTvII%3D)

* **【269页】前端大厂面试题宝典**  

  ![](https://p6-xtjj-sign.byteimg.com/tos-cn-i-73owjymdk6/4c6a0bed006c493eab17043e3c411624~tplv-73owjymdk6-jj-mark-v1:0:0:0:0:5o6Y6YeR5oqA5pyv56S-5Yy6IEAg55So5oi3MzM5MTQ5MjgwNjA=:q75.awebp?rk3s=f64ab15b&x-expires=1772527783&x-signature=AKr6egBqB69EQZSjpFJaKZpol6U%3D)


最后平时要进行自我分析与评价,做好职业规划,不断摸索,提高自己的编程能力和抽象思维能力。大厂面试远没有我们想的那么困难,摆好心态,做好准备,你也可以的。