【北大计算概论A】【期末复习】专题:大招

一些课上没有涉及的数据结构和算法其实可以快速的解题,但很多同学因为没有了解过而用很基础的算法写了复杂的代码,在考场上常因为debug困难并且烧脑拿不到分。本文希望速通一些很常用的算法,并且给出往年的考题和作业题来说明它的应用。

一、优先队列

在队列中,元素从队尾进入,从队首删除。相比队列,优先队列里的元素增加了优先级的属性,优先级高的元素先被删除。优先队列的内部是用堆实现的。(如果不了解堆可以忽略这句话)。

它的实现很简单,和队列极为相似,可以迅速上手。

优先队列会默认把较大的数据放在队首。

#include <iostream>
#include <queue>
using namespace std;
int main() {
	priority_queue<int> pq;
	pq.push(1); pq.push(2); pq.push(3);
	while (!pq.empty()) {
		cout << pq.top() << endl;
		pq.top();
	}
	return 0;
}

想要把较小的数据放在前面需要声明的时候写成这样:这个写法的含义是这个优先队列里装int,优先队列内部用vector存储,比较方式是greater。

priority_queue <int, vector<int>, greater<int> > pq;

优先队列的优先级重载:这是很常用的。这个例子中定义距离值较小的node优先级较高。(因为dist更大的较小)

struct node {
	int dist, loc;
	node () { }
	bool operator < (const node &a) const {
		return dist > a.dist;
	}
};
priority_queue<node> pq;

题目1:【期末模拟测试】拼装模型

题目描述

Dzx从日本回来了,并为TN准备了礼物----一个恐龙模型。TN想把它尽快拼好,但是由于模型很庞大,TN又实在比较懒,所以他希望你为他寻找一个最节省时间的拼装方案。

模型是由N个零件组成的,每次TN可以选取两个零件拼装在一起来组成一个新的零件,直到得到完整的模型。由于零件的复杂程度不同,TN每次拼装所需要的时间也是不同的,对于两个零件A和B,假设他们的复杂程度分别为a和b,则TN要将这两个零件拼装在一起所需要的时间为a+b,而这由两个零件所组成的新零件的复杂程度为a+b。

现在TN已经统计出了每个零件的复杂程度,你能告诉他最快的拼装方法需要多少时间么?

提示信息

每次都选择当前最小的两个零件。

关于输入

Line 1: N (1 <= N <= 10000),零件数目
Line 2: N Integers,表示每个零件的复杂程度

关于输出

最快的拼装方案所需要的时间

例子输入
3
1 2 9
例子输出
15

【题目分析】一种方法是每次都选择最少的两个元素,删除后把他们的和放回数组,再排序。这道题数据的范围不超过10000,时间复杂度O(nlogn)可以实现,但是当然有更好的解法,就是优先队列。这道题其实是优先队列最最经典的一道题,叫“合并果子”,甚至连样例都没有改。(不知道老师为什么会在这个时候考察这道题)

下面贴出合并果子的题面,其实和本题完全相同:

我们的想法就是每次取最小的两个移出堆,把它们的和加进答案以后再放进堆。

【代码实现】

#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int w[1005];
priority_queue<int, vector<int>, greater<int> > pq;
int main() {
    int n, ans = 0;
    cin >> n;
    for (int i = 0; i < n; i++) {
        cin >> w[i];
        pq.push(w[i]);
    }
    while (pq.size() >= 2) {
        int x = pq.top();
        pq.pop();
        x += pq.top();
        pq.pop();
        ans += x;
        pq.push(x);
    }
    cout << ans << endl;
    return 0;
}

题目2:【链表】链表合并排序

现在给出两个整数数列,先要将其合并为一个数列,并且合并后整个数列有序(从小到大),例如:
数列1: 3 1 2 5 9
数列2: 8 4 7 10 11
合并后得到数列:1 2 3 4 5 7 8 9 10 11
输入包括4行
第一行是1个整数N(N<=5000),表示数列1包含的整数个数
第二行是N个整数,表示数列1中的整数
第三行是1个整数M(M<=5000),表示数列2包含的整数个数
第四行是M个整数,表示数列2中的整数

【题目分析】虽然题目要用链表,但显然可以用优先队列,用优先队列最基础的功能函数即可。

【代码实现】

#include <iostream>
#include <cstdio>
#include <queue>
using namespace std;
priority_queue <int, vector<int>, greater<int> > pq;
int main() {
	int n, m;
	cin >> n;
	for (int i = 1; i <= n; i++) {
		int x;
		cin >> x;
		pq.push(x);
	}
	cin >> m;
	for (int i = 1; i <= m; i++) {
		int x;
		cin >> x;
		pq.push(x);
	}
	cout << pq.top();
	pq.pop();
	while (!pq.empty()) {
		cout << " " << pq.top();
		pq.pop();
	}
	return 0;
}

二、二分查找

这其实是用来降低复杂度的,基本思想大概在小学奥数时我们就接触过,每次找中间位置mid,然后看我们需要的数在[l, mid-1]还是正好mid还是[mid+1,r]区间中。

【模版】:查找a数组中是否有元素x,如果能找到返回数组下标

int find_x (int a[], int x, int n) { // n是a数组的元素个数,a数组下标从0开始且已经升序排好
	int l = 0, r = n - 1;
	while (l <= r) {
		int mid = (l + r) / 2;
		if (a[mid] == x) return mid;
		else if (a[mid] < x) l = mid + 1;
		else r = mid - 1; 
	}
	return -1; // 没有找到 
} 

题目1:【期末模拟测试】球桌出租

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值