参考书籍:
2025年1月3日期末考试题型:
一、选择题(20%)
二、填空(40%)
- 归并排序:循环体的条件,传参的范围
- 回溯算法:if条件,循环体条件,回溯语句(上下对称)
三、编程(40%)
考试范围:
第二章 二分搜索 归并排序 快速排序 找第K小数
第三章 矩阵连乘 最长公共子序列 单调递增最长子序列 0-1背包
第四章 会场安排问题 程序存储问题 最短路问题 背包问题
第五章 0-1背包 旅行售货员 n后问题 子集和问题
2025年一月的编程大题:
- 贪心算法,可套模版:程序存储问题
- 求LCS_length,套模板,较简单,不同点再与输入的是两个数组,if条件改成a[i]+b[i]=0,即原来是相同,现在是互为相反数
- 回溯法,较难,可以套01背包的回溯法的模板(定义结构体,按重量价值比给物品排序),不同点是物品的结构更复杂,如何存储输入的数据(结构体包含书本的名称,重量,价值)如何记录回溯的结果
选择题20分
- 动态规划的两个特征
- 贪心算法的两个特征
- 时间复杂度的计算
- 给f(x),g(x)表达式,判断是否同阶,上界,下界(第一章课后习题)
- 给代码判断时间复杂度
- NP类问题:是指一类可以用不确定性多项式算法求解的判定问题。
- 分支界限法和回溯法的区别,是广度优先搜索还是深度优先搜索
编程题:范围2-5章
1.递归分治——归并排序
挖空点:if(),while(),Merge(),MergeSort()括号内的条件/参数
2.贪心算法——程序存储问题
题目:设有n 个程序{1,2,…, n}要存放在长度为L的磁带上。程序i存放在磁带上的长度是 li,1≤i≤n。 程序存储问题要求确定这n个程序在磁带上的一个存储方案,使得能够在磁带上存储尽可能多的程序。 对于给定的n个程序存放在磁带上的长度,计算磁带上最多可以存储的程序数。
输入格式:
第一行是2 个正整数,分别表示文件个数n和磁带的长度L。接下来的1行中,有n个正整数,表示程序存放在磁带上的长度。
输出格式:
输出最多可以存储的程序数。
输入样例:
6 50
2 3 13 8 80 20
输出样例:
5
贪心策略:优先选择存放在磁带上长度最短的程序存储
#include <iostream>
#include <algorithm>
using namespace std;
int main() {
int n;
int l;
cin >> n >> l;
int* lens = new int[n+1];
for (int i = 0; i < n; i++) {
cin >> lens[i];
}
//程序长度从小到大排序
sort(lens, lens + n); //lens表示开始元素地址
int count = 0;
//每次选择长度短的程序,直到磁带存不下
for (int i = 0; i < n; i++) {
if (l < lens[i]) break;
l -= lens[i];
count++;
}
cout << count;
return 0;
}
3.动态规划——最长公共子序列
#include <iostream>
#include <string>
using namespace std;
int c[1000][1000]; //c[i][j]用来存储 Xi到Yj的最长公共子序列长度
void MaxLength(int m, int n, string x, string y) { //m,n分别表示字符串x和字符串y的长度
int i, j;
for (i = 0; i <= m; i++) // 当j为0的时候,空序列是Xi到Yj的最长公共子序列,所以赋值0,注意这里i从0开始
c[i][0] = 0;
for (i = 0; i <= n; i++) // 当x为0的时候,空序列是Xi到Yj的最长公共子序列,所以赋值0,注意这里i从0开始
c[0][i] = 0;
for (i = 1; i <= m; i++) {
for (j = 1; j <= n; j++) {
if (x[i - 1] == y[j - 1]) { // 索引要减1,因为字符串从0开始
c[i][j] = c[i - 1][j - 1] + 1;
} else {
c[i][j] = max(c[i - 1][j], c[i][j - 1]);
}
}
}
}
int main() {
string x, y;
cin >> x >> y;
int m = x.length(); // 得到字符串x的长度
int n = y.length(); // 得到字符串y的长度
MaxLength(m, n, x, y);
int precent = 0;
if (c[m][n]!= 0) {
precent = (c[m][n] * 100) / min(m, n);
}
cout << "公共长度:" << c[m][n] << endl;
return 0;
}
4.回溯法——0-1背包
给定n(n<=100)种物品和一个背包。物品i的重量是wi,价值为vi,背包的容量为C(C<=1000)。问:应如何选择装入背包中的物品,使得装入背包中物品的总价值最大? 在选择装入背包的物品时,对每种物品i只有两个选择:装入或不装入。不能将物品i装入多次,也不能只装入部分物品i。
输入格式:
共有n+1行输入: 第一行为n值和c值,表示n件物品和背包容量c; 接下来的n行,每行有两个数据,分别表示第i(1≤i≤n)件物品的重量和价值。
输出格式:
输出装入背包中物品的最大总价值。
输入样例:
5 10
2 6
2 3
6 5
5 4
4 6
输出样例:
15
#include <iostream>
#include <algorithm>
using namespace std;
struct Obj {
int weight;
int value;
};
int cp = 0, cw = 0, maxValue = 0;
int n, c;
Obj* objs;
//限界函数
float bound(int i) {
int cleft = c - cw;
int b = cp;
while (i <= n && objs[i].weight < cleft) {
b+= objs[i].value;
cleft -= objs[i].weight;
i++;
}
if (i <= n) {
b += objs[i].value * cleft / objs[i].weight;
}
return b;
}
//递归回溯函数
void backtrack(int t) {
if (t > n) {//叶子结点,得到问题的一个可能解
if (cp > maxValue) {
maxValue = cp;
}
return;
}
//如果满足约束条件进入左子树
if (cw + objs[t].weight <= c) {
cw += objs[t].weight;
cp += objs[t].value;
backtrack(t + 1);
cw -= objs[t].weight;
cp -= objs[t].value;
}
//如果满足限界条件进入右子树
if (bound(t + 1) > maxValue)
backtrack(t + 1);
}
//用于排序的比较函数,单位价值高的排在前面
bool cmp(Obj obj1, Obj obj2) {
if (float(obj1.value) / obj1.weight > float(obj2.value) / obj2.weight)
return true;
else
return false;
}
int main() {
int i;
cin >> n >> c;
objs = new Obj[n + 1];
for (i = 1; i <= n; i++) {
cin >> objs[i].weight>> objs[i].value;
}
sort(objs + 1, objs + n + 1, cmp);
backtrack(1);
cout << maxValue;
return 0;
}