保研考研机试攻略:第二章——入门经典(3)

🍦🍦🍦由于代码都是我自己敲出来调试的,所以可能不能一次更新那么多,大家见谅,不过因为我最近在备考机试,所以会拿出大量的时间在这上边,更新的会比较勤的~~~

目录

[🧊🧊🧊2.7 查找类问题](#🧊🧊🧊2.7 查找类问题)

🥥题型总结:

[🥥例题:DreamJudge 1476](#🥥例题:DreamJudge 1476)

[🥥例题:DreamJudge 1477](#🥥例题:DreamJudge 1477)

🥥练习题目:

[DreamJudge 1177 查找学生信息](#DreamJudge 1177 查找学生信息)

[DreamJudge 1388 查找 1](#DreamJudge 1388 查找 1)

[DreamJudge 1387 查找 - 北邮 🍰](#DreamJudge 1387 查找 - 北邮 🍰)

[DreamJudge 1383 查找第 K 小数](#DreamJudge 1383 查找第 K 小数)

[🧊🧊🧊2.8 贪心类问题](#🧊🧊🧊2.8 贪心类问题)

[🥥例题:DreamJudge 1478](#🥥例题:DreamJudge 1478)

🥥题型总结:

🥥练习题目:

[DreamJudge 1307 组队刷题 🍰](#DreamJudge 1307 组队刷题 🍰)

[DreamJudge 1347 To Fill or Not to Fill 🍰](#DreamJudge 1347 To Fill or Not to Fill 🍰)

[🧊🧊🧊2.9 链表类问题](#🧊🧊🧊2.9 链表类问题)

🥥题型总结:

[🥥例题:DreamJudge 1081](#🥥例题:DreamJudge 1081)

🥥练习题目:

[DreamJudge 1015 单链表](#DreamJudge 1015 单链表)

[DreamJudge 1018 击鼓传花](#DreamJudge 1018 击鼓传花)

[DreamJudge 1025 合并链表](#DreamJudge 1025 合并链表)

[DreamJudge 1405 遍历链表](#DreamJudge 1405 遍历链表)


🧊🧊🧊2.7 查找类问题

🥥题型总结:

  1. 数字查找: 给你一堆数字,让你在其中查找 x 是否存在( 如果 x 存在,请输出有几个)
  2. 字符串查找: 给你很多个字符串,让你在其中查找字符串 s 是否存在

当查询次数很多或数据量特别大的时候就不适合用顺序查找了。这类查找类题目,推荐使用map

🥥例题:DreamJudge 1476

对于查询量大的题目,我们有两种方法:一是将学号先排好序,然后使用二分查找,但是很多同学写二分的时候容易出现问题,而且代码量也比较大,不太推荐这种做法。二是使用map,推荐大家使用这种方法,基本上 map 可以通过 99.9%的这类题目。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
struct node{
    string num;
    string name;
    string sex;
    int age;
};
int main(){
    int n,q;
    map<string, node> M;//定义一个 map 映射
    while(scanf("%d", &n)!=EOF){
        for(int i=0;i<n;i++){
            node tmp;
            cin>>tmp.num>>tmp.name>>tmp.sex>>tmp.age;
            M[tmp.num] = tmp;//将学号指向对应的结构体
        }
        scanf("%d", &q);
        for(int i=0;i<q;i++){
            string num;
            cin>>num;
            if((M.find(num))!=M.end())//find 查找 如果找不到则返回末尾
                cout<<M[num].num<<" "<<M[num].name<<" "<<M[num].sex<<" "<<M[num].age<<endl;
            else
                cout<<"No Answer!"<<endl;
        }
    }
    return 0;
}

上边是静态查找,接下来我们看一道动态查找的问题:

🥥例题:DreamJudge 1477

分析这道题目,这道题的一大特点就是,数的集合在不断的改变。如果用先排序再二分的方法就会很困难,因为加入新的数时需要去多次移动数组才能将数插进去,最坏情况每次插入都是 O(n)的复杂度,这是无法接受的。当然也不是说不能用这样的方法来解决,可以用离线的方法来解决这个问题,但是这样做太复杂,不适合在考试中使用。那么我们考虑用 map 来解决这个问题。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
int main(){
    int n,q,x;
    map<int, int> M;//定义一个 map 映射
    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &x);
        M[x]++;//记录集合中 x 有多少个
    }
    scanf("%d", &q);
    for (int i = 0; i < q; i++) {
        scanf("%d", &x);
        if (M[x] == 0) {//如果 x 的个数为 0
            printf("no\n");
            M[x]++;//将 x 加入到集合中
        }
        else printf("find\n");
    }
    return 0;
}

接下来还是说说二分这个方法,二分的前提是单调性,只要满足单调性就可以二分,不论是单个元素还是连续区间。下面给出二分的代码供参考:

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
int a[10005];
int main(){
    int n,x;
    scanf("%d", &n);//输入 n 个数
    for (int i = 1; i <= n; i++) {
        scanf("%d", &a[i]);
    }
    sort(a+1, a+1+n);//排序保持单调性
    scanf("%d", &x);//要查找的数 x 13. int l = 1, r = n;
    while (l <= r) {
        int mid = (l + r) / 2;
        if (a[mid] == x) {
            printf("find\n");
            return 0;
        }
        if (a[mid] > x) {//如果 x 比中间数小
            r = mid - 1;//说明在左区间
        }
        else l = mid + 1;//否则在右区间内
    }
    printf("not find\n");
    return 0;
}

🥥练习题目:

DreamJudge 1177 查找学生信息

这道题目其实不用map,直接使用两个数组,一个数组记录每个人借阅了哪本书,另一个数组记录每本书的借阅情况,然后查找即可。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,m;
	while(cin>>n>>m)
	{
		vector<int> a(n+5),b(m+5);
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
			b[a[i]]++;
		}
		for(int i=1;i<=n;i++)
		{
			if(b[a[i]]-1) cout<<b[a[i]]-1<<endl;
			else cout<<"BeiJu"<<endl;
		}
	}
	return 0;
}

DreamJudge 1388 查找 1

这道题目用不到map,因为只需要记录一个数据,直接使用set进行查找即可,也是用find进行查找,如果需要记录二维信息,那么用map。

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,m;
	while(cin>>n)
	{
		set<int> s;
		int cur;
		for(int i=0;i<n;i++)
		{
			cin>>cur;
			s.insert(cur);
		}
		cin>>m;
		for(int i=0;i<m;i++)
		{
			cin>>cur;
			if(s.find(cur)!=s.end()) cout<<"YES"<<endl;
			else cout<<"NO"<<endl;
		}
	}
	return 0;
}

DreamJudge 1387 查找 - 北邮 🍰

cpp 复制代码
#include<iostream>
#include<string>
using namespace std;
string s;
int n;

void pd1(int i,int len)
{
    int left=i,right=i+len-1;//翻转范围
    for (int j=1;j<=len/2;j++)
	{
        char tmp=s[left];
        s[left]=s[right];
        s[right]=tmp;
        left++;
        right--;
    }
}


int main()
{
    while(cin>>s)
	{
        cin>>n;
        while(n--)
		{
            string op;
            cin>>op;

            int i=op[1]-'0';
            int len=op[2]-'0';

            if(op[0]-'0'==0) pd1(i,len);
            else 
			{
                string rep=op.substr(3,op.size());
                s.erase(i,len);
                s.insert(i,rep);
            }
            cout<<s<<endl;
        }
    }
    return 0;
}

DreamJudge 1383 查找第 K 小数

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,k,cur;
	set<int> s;
	while(cin>>n)
	{
		for(int i=0;i<n;i++) 
		{
			cin>>cur;
			s.insert(cur);
		}
		cin>>k;
		k--;
		auto it=s.begin();
		while(k--) it++;
		cout<<*it<<endl;
	}
	return 0;
}

🧊🧊🧊2.8 贪心类问题

贪心算法更重要的是一种贪心的思想,它是通过当前最优解,得到全局最优解。

我们先通过几个例子感受一下贪心思想:

例子 1:地上有 3 张纸币,分别是 5 元、1 元和 10 元,问你只能拿一张,最多能拿多少钱?

解析:很明显,10 元。

例子 2:地上有 n 张纸币,有 1 元的,有 5 元的,还要 10 元的,问你只能拿一张,最多能拿多少钱?

解析:很明显,还是 10 元。

例子 3:地上有很多纸币,有 a 张 1 元的,有 b 张 5 元的,还要 c 张 10 元的,问你从中拿x张,最多能拿多少钱?

解析:大家应该都能想到,肯定是优先拿 10 元的,如果 10 元的拿完了,再拿 5 元的,最后才会拿 1 元的。这就是贪心的思想,所以贪心其实是很容易想到的。

例子 4:有 n 个整数构成的集合,现在从中拿出 x 个数来,问他们的和最大能是多少?

解析:相信大家都能想到,优先拿大的,从大到小一个个拿,这样组成的和最大。那么在解决这个问题之前,我们需要先排序,从大到小的排好序,然后将前 x 个数的和累加起来就是答案。

使用贪心时,往往需要按照某个特性先排好序,所以贪心一般和sort、堆等一起使用。

🥥例题:DreamJudge 1478

通过分析可以发现,小明想要喝尽量多的饮料,肯定优先选择性价比最高的饮料喝,也就是说先喝1毫升的价格最低的饮料,所以就需要去比较每种饮料1毫升的价格是多少,按照这个单价从低到高依次排序,然后一个一个往后喝,这样可以保证小明能喝到最多的饮料。

cpp 复制代码
#include <bits/stdc++.h>
using namespace std;
struct node {
    double w, m;
}p[1005];
bool cmp(node a, node b) {
//按照每毫升的价格从低到高排序
    return a.w/a.m < b.w/b.m;
}
int main(){
    int n,x;
    while (scanf("%d%d", &x, &n) != EOF) {
        if (x == -1 && n == -1) break;
        for (int i = 1; i <= n; i++) {
            scanf("%lf%lf", &p[i].m, &p[i].w);
        }
        sort(p+1, p+1+n, cmp);
        double ans = 0;
        for (int i = 1; i <= n; i++) {
            if (x >= p[i].w) {//如果剩余的钱能全买
                ans += p[i].m;
                x -= p[i].w;
            }
            else {//如果剩余的钱买不完这种饮料
                ans += (p[i].m*x/p[i].w);
                break;//到这里 x 已经为 0 了
            }
        }
        printf("%.3lf\n", ans);
    }
    return 0;
}

🥥题型总结:

贪心题目看起来简单但做起来难的原因原因有二:一是就没有想到这个题目应该用贪心算法,没能将题目抽象成数学模型来分析。二是读懂题了,知道应该用贪心算法解题,但排序的特征点没有找准,因为不是所有题目都能明显看出从小到大排序,有的题目可能隐藏的比较深,但是这种难度的贪心不常见。所以机试中的贪心题,只要反应过来是用贪心,99%的情况下都能解决。

🥥练习题目:

DreamJudge 1307 组队刷题 🍰

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
struct node
{
	double w;//题目
	double m;//精力
}a[200];
bool cmp(node a,node b){ return a.w/a.m>b.w/b.m;}
int main()
{
	int m,n;
	while(cin>>m>>n)
	{
		if(m==-1&&n==-1)
			break;
		for(int i=0;i<n;i++) cin>>a[i].w>>a[i].m;
		sort(a,a+n,cmp);
		double cnt=0;
		for(int i=0;i<n;i++)
		{
			if(m>a[i].m)
			{
				m-=a[i].m;
				cnt+=a[i].w;
			}
			else
			{
				cnt+=m/a[i].m*a[i].w;
				break;
			}
		}
		printf("%.3lf\n",cnt);//用cout<<fixed<<setpercision(3)<<cnt会超时
	}
	return 0;
}

DreamJudge 1347 To Fill or Not to Fill 🍰

输出:

复制代码
749.17
The maximum travel distance = 1200.00
cpp 复制代码
/*
摘自N诺评论区大神:mimicool
我们假设会经过所有的加油站
我们可以假装持有价格高得油,一旦遇到价格低的就进行替换
在经过加油站的时候,我们补充该加油站的油(注意是假设补充,具体是否补充要看之后有没有遇见更加便宜的油)比如,我们的油箱在遇到价值4快的加油站时,还剩下2块钱的油和5块钱的油,这说明之前假装补充的5块钱的油是可以被四块钱的油所替代,因此我们就可以补充油注意计算cost的时候要以实际花费的油为准,而不是在补充油的时候进行计算
*/

#include<bits/stdc++.h>
using namespace std;

// double判断相等需要用一个接近无穷小
#define eps 1e-8

//加油站结构体
struct Station{
    double price, distance;
}s[200];

bool cmp(Station s1, Station s2){
    return s1.distance < s2.distance;
}

int main(){
    double cmax, d, davg;
    int n;
    while (scanf("%lf%lf%lf%d", &cmax, &d, &davg, &n) != EOF){
        // 从1开始,较为清晰
        for (int i = 1; i <= n; i++){
            scanf("%lf%lf", &s[i].price, &s[i].distance);
        }
        //终点看作特殊的加油站
        s[n+1].distance = d;
        s[n+1].price = 0;
        //注意这里的上下界
        sort(s+1, s+1+n+1, cmp);
        //双端队列,一方面弹出最低价格的油作为消耗;另一方面弹出最高价格的油作为替换
        deque<pair<double, double>> d;
        double sumdis = 0., cost = 0.;
        if (s[1].distance > eps) printf("The maximum travel distance = 0.00\n");
        else {
            d.push_back(make_pair(s[1].price, cmax));
            for (int i = 2; i <= n+1; i++){
                //目标第i个加油站
                double sdis = s[i].distance - s[i-1].distance; //距离目标加油站的距离
                double need = sdis / davg; //需要使用的油
                //printf("%d, %lf, %lf\n", i, sdis, need);
                if (need-cmax > eps){
                //油不够用
                    sumdis += cmax*davg;
                    printf("%s%.2lf", "The maximum travel distance = ", sumdis);
                    break;
                }else{
                    //油够用
                    sumdis += sdis;
                    //add是最后要添加的油,计算过程:使用的油+高价的油
                    double add = need;
                    while (need > eps){
                        //这部分是用来计算cost和更新队列原有油信息的
                        if (d.front().second-need > eps){
                            //需求小于价格最低的油量,意味着无需弹出队列
                            cost += need*d.front().first;
                            d.front().second -= need;
                            need = 0;
                        }else {
                            //需求大于价格最低的油量,意味着需要弹出队列并进行循环
                            cost += d.front().first*d.front().second;
                            need -= d.front().second;
                            d.pop_front();
                        }
                    }
                    while(!d.empty() && d.back().first > s[i].price){
                        //这部分使用while循环遍历,找出所有高价油弹出
                        add += d.back().second;
                        d.pop_back();
                    }
                    //用当前加油站油替换
                    d.push_back(make_pair(s[i].price, add));
                }
                //如果此时到达了终点,就打印cost
                if (i == n+1) printf("%.2lf\n", cost);
            }
        }
    }
    return 0;
}

🧊🧊🧊2.9 链表类问题

链表类问题属于选读章节,对于使用 OJ 测评的院校的同学来说,这类问题可以用数组来实现,没有必要用链表去实现,慢且易出错,所以一般都直接用数组实现,反正最后能 AC 就行,这部分同学跳过本节或仅做了解即可。但是对于非 OJ 测评的院校来说,链表类问题可以说是必考的题型。

🥥题型总结:

  1. **猴子报数:**循环链表建立之后,按照题意删除节点。
  2. **两个有序链表合并为一个:**这个和两个有序数组合并为一个有序数组原理一样。
  3. **链表排序:**使用冒泡排序进行链表排序,因为冒泡排序是相邻两个元素进行比较交换,适合链表。

🥥例题:DreamJudge 1081

需要创建一个首尾相连的循环链表,先走 s 步,再开始循环遍历链表,每走 m 步删除一个节点,直到链表中只剩下一个节点时结束循环。只剩一个节点的判断条件是,它的下一个指针指向的是自己,说明自循环了。

cpp 复制代码
#include <stdio.h>
#include <malloc.h>
struct node {
    int num;
    struct node *next;
};
int n, s, m;
//创建循环链表
struct node* create() {
    struct node *head, *now, *pre;
    for (int i = 1; i <= n; i++) {
        now = (struct node *)malloc(sizeof(node));
        if (i == 1) {
            head = now;
            pre = now;
        }
        now->num = i;
        now->next = head;
        pre->next = now;
        pre = now;
    }
    return head;
};
//按照题目要求输出
void print(struct node *head) {
    struct node *p, *pre;//pre 是前一个节点
    p = head;
    s -= 1;//因为起点在第一个 所以要少走一步
    while (s--) {//先走 s 步
        pre = p;
        p = p->next;
    }
    int i = 1;
    while (p != NULL) {
        if (p == p->next) {//只剩最后一个
            printf("%d\n", p->num);
            break;
        }
        if (i % m == 0) {//找到第 m 个
            printf("%d,", p->num);//输出它
            pre->next = p->next;//删除它
        }
        else pre = p;//这里一定要用 else 如果是删除的话 pre 不变
        p = p->next;
        i++;
    }
}
int main(){
    while (scanf("%d%d%d", &n, &s, &m) != EOF) {
        if (n==0&&s==0&&m==0) break;
        struct node *head;
        head = create();
        print(head);
    }
    return 0;
}

🥥练习题目:

DreamJudge 1015 单链表

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
	int a[10]={0};
	for(int i=0;i<5;i++) cin>>a[i];
	sort(a,a+5);
	for(int i=0;i<5;i++) cout<<a[i]<<" ";
	return 0;
}

DreamJudge 1018 击鼓传花

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n;
	cin>>n;
	queue<int> q;
	int cur;
	for(int i=1;i<=n;i++) q.push(i);
	while(!q.empty())
	{
		for(int i=0;i<2;i++)
		{
			int c=q.front();
			q.pop();
			q.push(c);
		}
		cur=q.front();
		q.pop();
	}
	cout<<cur;
	return 0;
}

DreamJudge 1025 合并链表

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
	vector<int> a;
	int n,m,cur;
	cin>>n;
	for(int i=0;i<n;i++) 
	{
		cin>>cur;
		a.push_back(cur);
	}
	cin>>m;
	for(int i=0;i<m;i++)
	{
		cin>>cur;
		a.push_back(cur);
	}
	sort(a.begin(),a.end());
	for(int i=0;i<n+m;i++) cout<<a[i]<<" ";
	return 0;
}

DreamJudge 1405 遍历链表

cpp 复制代码
#include<bits/stdc++.h>
using namespace std;
int main()
{
	int n,a[1010]={0};
	cin>>n;
	for(int i=0;i<n;i++) cin>>a[i];
	sort(a,a+n);
	for(int i=0;i<n;i++) cout<<a[i]<<" ";
	return 0;
}

创作不易,点个赞吧~点赞收藏不迷路,感兴趣的宝子们欢迎关注该专栏~

这部分的练习题特别多,我后边会尽快补齐的,做完我就更新啦,大家可以先自己练习着~

勤奋努力的宝子们,学习辛苦了!🌷🌷🌷休息下,我们下部分再见👋( •̀ ω •́ )✧~

相关推荐
苏纪云21 小时前
蓝桥杯知识点——day2
数据结构·算法·蓝桥杯
Wect21 小时前
LeetCode 52. N 皇后 II:回溯算法高效求解
前端·算法·typescript
读研的武21 小时前
Golang学习笔记 入门篇
笔记·学习·golang
iFlyCai21 小时前
数据结构与算法之希尔排序
数据结构·算法·排序算法
自传丶21 小时前
【学习笔记】大模型应用开发系列(二)Embedding 模型
笔记·学习·embedding
lcreek21 小时前
LeetCode2208. 将数组和减半的最少操作次数、LeetCode2406.将区间分为最少组数
python·算法
shehuiyuelaiyuehao21 小时前
算法1,移动零
数据结构·算法·排序算法
shehuiyuelaiyuehao1 天前
算法2,复写零
数据结构·算法
像污秽一样1 天前
算法设计与分析-算法效率分析基础-习题1.1
c语言·数据结构·c++·算法
abant21 天前
leetcode 739 单调栈模板题
算法·leetcode·职场和发展