A 全排列
- 简单的递归。
- 设置数组 p 存储当前得到的排列,散列表 h 存储当前数是否已加入排列,标志位 index 表示数组 p 中已存储数的个数。若 index == n, 说明所有数字已经排列在 p 中,直接输出即可。否则,试探将某个数加入排列:若这个数的散列值为 1 ,说明已经在排列中了,否则将这个数加入排列,同时该数的散列值置 1,然后递归求 p 的第 index +1 位置的数,递归结束后要将当前加入的数的散列值还原。
- 也可以使用 STL 中的 next_permutation() 。
- 参考代码如下。
#include<iostream>
using namespace std;
void permu(int p[], int n, int h[], int index) {
if (index == n) {
for (int i = 0; i < n - 1; i++)
cout << p[i] << ' ';
cout << p[n - 1] << endl;
}
else {
for (int i = 1; i <= n; i++) {
if (!h[i]) {
p[index] = i;
h[i] = 1;
permu(p, n, h, index + 1);
h[i] = 0;
}
}
}
}
int main() {
int n;
int p[10], h[10] = { 0 };
cin >> n;
permu(p, n, h, 0);
return 0;
}
B 组合的输出
- 题目说了不用递归的方法?!!!∑(゚Д゚ノ)ノ可是我只会递归啊。
- 将全排列的程序稍微改下就可以啦。函数设置参数 r ,index == r 时就输出。然后 p 中添加某个数时要求该数比 p 中末尾的数要大。就这两点改动。
- 参考代码如下。
#include<iostream>
using namespace std;
void solve(int p[], int n, int r, int h[], int index) {
if (index == r) {
for (int i = 0; i < r - 1; i++)
cout << p[i] << ' ';
cout << p[r - 1] << endl;
}
else {
for (int i = 1; i <= n; i++) {
if (!h[i] && (index == 0 || p[index - 1] < i)) {
p[index] = i;
h[i] = 1;
solve(p, n, r, h, index + 1);
h[i] = 0;
}
}
}
}
int main() {
int n, r;
int p[30], h[30] = { 0 };
cin >> n >> r;
solve(p, n, r, h, 0);
return 0;
}
C 组合+判断素数
- 任意给出一组数 b,对其进行排列组合。
- 将 B 中代码稍微修改下就可以啦。首先添加一个判断素数的函数。其次在生成排列的过程中,添加变量 cur,用来指示 p 数组末尾的数在 b 中的下标,当在 p 中添加 b 中的某个数时,要求 cur 不大于这个数的下标。另外,这里将 b 数组,p 数组,h 数组等设为了全局变量,因此函数传参个数可以少一些。
- 参考代码如下。
#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
int b[30], p[30], h[30], cnt = 0, cur = 0;
bool prime(int n) {
if (n == 0 || n == 1) return false;
else {
int r = sqrt(n);
for (int i = 2; i <= r; i++)
if (n % i == 0) return false;
return true;
}
}
void solve(int n, int r, int index) {
fill(h, h + 30, 0);
if (index == r) {
int sum = 0;
for (int i = 0; i < r; i++)
sum += p[i];
if (prime(sum)) cnt++;
}
else for (int i = 0; i < n; i++) {
if (!h[i] && (index == 0 || cur < i)) {
p[index] = b[i];
cur = i; h[i] = 1;
solve(n, r, index + 1);
cur = 0; h[i] = 0;
}
}
}
int main() {
int n, k;
cin >> n >> k;
for (int i = 0; i < n; i++)
cin >> b[i];
solve(n, k, 0);
cout << cnt << endl;
return 0;
}
D n 皇后问题(原始的 8 皇后问题)
- 很简单啦。暴力 + 回溯,参考代码如下。
#include<iostream>
#include<algorithm>
using namespace std;
int p[12], h[12] = { 0 }, cnt = 0;
void solve(int n, int r) {
if (r == n) {
cnt++;
for (int i = 0; i < n - 1; i++)
cout << p[i] << ' ';
cout << p[n - 1] << endl;
}
else for (int i = 1; i <= n; i++) {
if (!h[i]) {
bool flag = true;
for (int j = 0; j < r; j++)
if (abs(r - j) == abs(i - p[j])) {
flag = false;
break;
}
if (flag) {
p[r] = i;
h[i] = 1;
solve(n, r + 1);
h[i] = 0;
}
}
}
}
int main() {
int n;
cin >> n;
solve(n, 0);
if (!cnt)
cout << "no solute!" << endl;
return 0;
}
E 出栈序列统计
- 求卡特兰数。如果知道卡特兰数公式 ,直接算就行。
- 栈混洗的结果数也是卡特兰数,如果可以得到栈混洗的结果数,自然就得到了卡特兰数。
- 得到栈混洗的结果有两种方法,一种是全排列,然后验证排列是否为栈混洗;另一种则是直接模拟栈混洗的过程:设定一个队列 q, 一个中间栈 t, 一个保存结果的栈 s。如果 s 大小为 n,说明已经得到一个栈混洗排列,计数加 1;否则要么 q 出队,要么 t 弹栈,递归模拟栈混洗,递归结束时要恢复回溯。
- 参考代码如下。
#include<iostream>
#include<vector>
#include<stack>
#include<queue>
using namespace std;
int cnt = 0;
void solve(int n, queue <int> q, stack <int> t, stack <int> s) {
if (s.size() == n)
cnt++;
else {
if (q.size()) {
t.push(q.front());
q.pop();
solve(n, q, t, s);
q.push(t.top());
t.pop();
}
if (t.size()) {
s.push(t.top());
t.pop();
solve(n, q, t, s);
t.push(s.top());
s.pop();
}
}
}
int main() {
int n;
stack <int> t, s;
queue <int> q;
cin >> n;
for (int i = 1; i <= n; i++)
q.push(i);
solve(n, q, t, s);
cout << cnt;
return 0;
}
F 走迷宫
- DFS 递归求路径的常规题。需要设置一个数组来存储路径,当到达终点时即可输出路径。
- 参考代码如下。
#include<iostream>
#include<vector>
using namespace std;
int X[4] = { 0,-1,0,1 };
int Y[4] = { -1,0,1,0 };
int n, m;
struct node {
int x, y;
int node;
bool visited = false;
}maze[20][20];
vector <node> v;
bool no_path = true;
bool is_path(int x, int y) {
if (x < 1 || x > n || y < 1 || y > m) return false;
if (maze[x][y].visited || maze[x][y].node == 0) return false;
return true;
}
void dfs(int x1, int y1, int x2, int y2) {
if (x1 == x2 && y1 == y2) {
no_path = false;
for (int i = 0; i < v.size(); i++)
cout << '(' << v[i].x << ',' << v[i].y << ')' << "->";
cout << '(' << x2 << ',' << y2 << ')' << endl;
}
else for (int i = 0; i < 4; i++) {
int xt = x1 + X[i];
int yt = y1 + Y[i];
if (is_path(xt, yt)) {
maze[x1][y1].visited = true;
v.push_back(maze[x1][y1]);
dfs(xt, yt, x2, y2);
maze[x1][y1].visited = false;
v.pop_back();
}
}
}
int main() {
int x1, y1, x2, y2;
cin >> n >> m;
for (int x = 1; x <= n; x++)
for (int y = 1; y <= m; y++) {
cin >> maze[x][y].node;
maze[x][y].x = x;
maze[x][y].y = y;
}
cin >> x1 >> y1 >> x2 >> y2;
dfs(x1, y1, x2, y2);
if (no_path) cout << -1 << endl;
return 0;
}