第五章
- C++中结构体的使用:类似与类,可以定义构造函数,重载运算符。
- qsort的使用: 其默认是增续排列,自定义比较函数,该函数使用时不拘一格。
第六章
- ACM题目中尽量使用数组来实现链表。
- 指针访问比用“数组+下标”方式略快。
第七章
- 生成1~n排列的方法
- 基本思想是用递归,先输出所有以1开头的排列,然后输出所有以2开头的排列,最后输出以n开头的。伪代码如下:
void print_permutation(序列A, 集合S)
{
if(S 为空) 输出序列A;
else 按照从小到大的顺序依次考虑S的每个元素v
{
print_permutation(在A的末尾加上v得新序列,s-{v})
}
}
对集合S的元素进行排序,然后统计各个元素出现的个数,可以得到生成可重集的程序:
代码
#include<stdio.h>
int P[100], A[100];
// 输出数组P中元素的全排列。数组P中可能有重复元素, 数组P中的元素需要已经按顺序排好了
void print_permutation(int n, int* P, int* A, int cur) {
int i, j;
if(cur == n) {
for(i = 0; i < n; i++) printf("%d ", A[i]);
printf("\n");
}
else for(i = 0; i < n; i++) //因为是全排列,所以每次大循环是必要的。从中选去最小元素
if(!i || P[i] != P[i-1])
{
int c1 = 0, c2 = 0;
for(j = 0; j < cur; j++) if(A[j] == P[i]) c1++; //判断该元素是否还能使用
for(j = 0; j < n; j++) if(P[i] == P[j]) c2++;
if(c1 < c2) {
A[cur] = P[i];
print_permutation(n, P, A, cur+1);
}
}
}
int main() {
int i, n;
scanf("%d", &n);
for(i = 0; i < n; i++)
scanf("%d", &P[i]);
//sort(P, P+n);
print_permutation(n, P, A, 0);
return 0;
}
STL提供了 next_permutation函数,可以按照字典顺序生成排列:
代码
#include<cstdio>
#include<algorithm>
using namespace std;
int main() {
int n, p[10];
scanf("%d", &n);
for(int i = 0; i < n; i++) scanf("%d", &p[i]);
sort(p, p+n); // 排序,得到p的最小排列
do {
for(int i = 0; i < n; i++) printf("%d ", p[i]); // 输出排列p
printf("\n");
} while(next_permutation(p, p+n)); // 求下一个排列
return 0;
}
- 枚举子集的三种方法:
- 增量构造法:每次首先打印当前子集合,然后尝试按照定序选择一个元素放入当前集合中。然后递归调用下去。该方法解答树节点为2^n.
代码
#include<stdio.h>
int B[5]={1,3,5,7,9};
int A[10];
int C[10] = {0,0,0,1,0,2,0,3,0,4}; // 该数组记录数组B中个元素的下标
void print_subset(int n, int cur)
{
for(int i = 0; i < cur; i++)
printf("%d ", A[i]); // 打印当前集合
printf("\n");
int s = cur ? C[A[cur-1]] +1 : 0 ;// 确定当前元素的最小可能值 这里使用了定序技巧
for(int i = s; i < n; i++)
{
A[cur] = B[i];
print_subset(n,cur+1); // 递归构造子集
}
}
int main() {
print_subset(5, 0);
return 0;
} - 位向量法:构造位向量B[i], 仍然使用递归,每步生成位向量一位,直至N位时,根据B打印结果。该方法解答树节点为2^(n+1),因为包含了中间结果,所以比上一种方法多了一倍节点。
代码
#include<stdio.h>
void print_subset(int n, int* B, int cur) {
if(cur == n) {
for(int i = 0; i < cur; i++)
if(B[i]) printf("%d ", i); // 打印当前集合
printf("\n");
return;
}
B[cur] = 1; // 选第cur个元素
print_subset(n, B, cur+1);
B[cur] = 0; // 不选第cur个元素
print_subset(n, B, cur+1);
}
int B[10];
int main() {
print_subset(5, B, 0);
return 0;
} - 二进制法: 思想和第二种方法一样,不过使用32位INT 即内置类型作为位向量使用。利用计算机位运算以及内部为二进制的优势,效率也为 2^n的效率。
代码
#include<stdio.h>
void print_subset(int n, int s) { // 打印{0, 1, 2, ..., n-1}的子集S
for(int i = 0; i < n; i++)
if(s&(1<<i)) printf("%d ", i); // 这里利用了C语言“非0值都为真”的规定
printf("\n");
}
int main() {
int n = 5;
for(int i = 0; i < (1<<n); i++) // 枚举各子集所对应的编码 0, 1, 2, ..., 2^n-1
print_subset(n, i);
return 0;
}
- 回溯法: 使用时注意一定要在递归调用后将递归调用前设置的全局辅助(VIS)变量重置。
- 隐式图遍历: BFS。 迭代加深搜索:每次决定搜索的深度(递增),然后使用DFS进行搜索。
第八章
- 二分查找:
while(x < y)
{
m = x + (y - x) / 2;
if(F(m)) y = m;
else x = m + 1;
}由于 / 对整型取整是朝零取整而不是向下取整, 即-5/2是2 所以使用x + (y - x) / 2 则可以保证分界点总是靠近区间的起点。如果使用(x + y) / 2则不行(当x+y小于零时, 所求的中点会靠近终点)
- 最大值最小化: 通过二分和贪心,将最优化问题转换为二分查找问题, 应该也是数值方法的思想。

浙公网安备 33010602011771号