算法竞赛入门经典第7章
1 生成遍历法:先产生所有的可能,然后根据限制条件过滤结果
#define RUN
#ifdef RUN
#include<stdio.h>
//C[x]=y 表示x行的皇后处于y列
int C[50], tot = 0, n = 8, nc = 0;
void search(int cur) {
int i, j;
nc++;
//检查每个可能,看是否会冲突
if(cur == n) {
for(i = 0; i < n; i++)
for(j = i+1; j < n; j++)
if(C[i] == C[j] || i-C[i] == j-C[j] || i+C[i] == j+C[j]) return;
//找到一个没冲突的可能
tot++;
}
//遍历生成所有的可能
else for(i = 0; i < n; i++) {
C[cur] = i;
search(cur+1);
}
}
int main() {
search(0);
printf("%d\n", tot);
printf("%d\n", nc);
return 0;
}
#endif
2 回溯法
//#define RUN
#ifdef RUN
#include<stdio.h>
//C[x]=y 表示x行的皇后处于y列
int C[50], tot = 0, n = 8, nc = 0;
void search(int cur) {
int i, j;
nc++;
//递归边界,只要走到这里,所有的皇后必然不冲突
if(cur == n) {
tot++;
}
else {
//对于当前第cur行,逐个测试i从0到n-1,看适合放在哪一列
for(i = 0; i < n; i++) {
int ok = 1;
//假设放在第i列
C[cur] = i;
//测试在前面的行的皇后是否会和当前位置冲突
for(j = 0; j < cur; j++){
//列冲突,主对角线冲突,副对角线冲突
if(C[cur] == C[j] || cur-C[cur] == j-C[j] || cur+C[cur] == j+C[j]) {
ok = 0;
break;
}
}
//进行下一行的搜索
if(ok) search(cur+1);
}
}
}
int main() {
search(0);
printf("%d\n", tot);
printf("%d\n", nc);
return 0;
}
#endif
3 有全局变量的回溯法(最常用)
//#define RUN
#ifdef RUN
#include<stdio.h>
#include <string.h>
//C[x]=y 表示x行的皇后处于y列
int C[50], vis[3][50], tot = 0, n = 8, nc = 0;
void search(int cur) {
int i, j;
nc++;
if(cur == n) {
tot++;
}
else{
//测试每一列
for(i = 0; i < n; i++) {
//检测列冲突,副对角线,正对角线冲突
if(!vis[0][i] && !vis[1][cur+i] && !vis[2][cur-i+n]) {
C[cur] = i;
vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 1;
search(cur+1);
vis[0][i] = vis[1][cur+i] = vis[2][cur-i+n] = 0;
}
}
}
}
int main() {
memset(vis, 0, sizeof(vis));
search(0);
printf("%d\n", tot);
printf("%d\n", nc);
return 0;
}
#endif
注意:如果在回溯法中使用了辅助的全局变量,则一定要及时把它们恢复原状。例如,若函数有多个出口,则需在每个出口处恢复被修改的值。
3万+

被折叠的 条评论
为什么被折叠?



