第二部分 基础算法 --> 第五章 搜索与回溯算法
搜索与回溯算法
1317 【例5.2】组合的输出
【题目描述】
排列与组合是常用的数学方法,其中组合就是从 n 个元素中抽出 r 个元素(不分顺序且 r ≤n),
我们可以简单地将 n 个元素理解为自然数 1,2,…,n,从中任取 r 个数。
现要求你用递归的方法输出所有组合。
【输入】一行两个自然数 n、r(1<n<21,1≤r≤n)。
【输出】所有的组合,每一个组合占一行且其中的元素按由小到大的顺序排列,
每个元素占三个字符的位置,所有的组合也按字典顺序。
【输入样例】
5 3
【输出样例】
1 2 3
1 2 4
1 2 5
1 3 4
1 3 5
1 4 5
2 3 4
2 3 5
2 4 5
3 4 5
【参考程序】
#include<bits/stdc++.h>
using namespace std;
const int N=1e3;
int a[N], vis[N], n, r;
//第 m 个数
void dfs(int m){
if(m>r){
for(int i=1; i<=r; i++){
cout<<setw(3)<<a[i];
}cout<<endl;
return;
}
for(int i=1; i<=n; i++){
if(vis[i]==0 && a[m-1]<i){
vis[i]=1;
a[m] = i;
dfs(m+1);
vis[i] = 0;
}
}
}
int main(){
cin>>n>>r;
dfs(1);
return 0;
}
1318 【例5.3】自然数的拆分
【题目描述】任何一个大于 1 的自然数 n,总可以拆分成若干个小于 n 的自然数之和。
【输入】输入n。
【输出】按字典序输出具体的方案。
【输入样例】
7
【输出样例】
7=1+1+1+1+1+1+1
7=1+1+1+1+1+2
7=1+1+1+1+3
7=1+1+1+2+2
7=1+1+1+4
7=1+1+2+3
7=1+1+5
7=1+2+2+2
7=1+2+4
7=1+3+3
7=1+6
7=2+2+3
7=2+5
7=3+4
【参考程序】
#include<bits/stdc++.h>
using namespace std;
int n,total=0;
const int N=1e3;
int a[N];
//现有和, 选择第 m 个数
void dfs(int sum, int m) {
if(sum>n) return;
if(sum==n && m>2) {
cout<<n<<"=";
for(int i=1; i<m-1; i++) {
cout<<a[i]<<"+";
}
cout<<a[m-1]<<endl;
return ;
}
for(int i=1; i<=n; i++) {
if(sum+i<=n && a[m-1]<=i) {
a[m] = i;
dfs(sum+i, m+1);
a[m] = 0;
}
}
}
int main() {
cin>>n;
dfs(0,1);
return 0;
}
1212 LETTERS
【题目描述】
给出一个 roe×col 的大写字母矩阵,一开始的位置为左上角,
你可以向上下左右四个方向移动,并且不能移向曾经经过的字母。
问最多可以经过几个字母。
【输入】第一行,输入字母矩阵行数 R 和列数 S,1≤R,S≤20。
接着输出 R行 S列字母矩阵。
【输出】最多能走过的不同字母的个数。
【输入样例】
3 6
HFDFFB
AJHGDH
DGAGEH
【输出样例】
6
【参考程序】
#include <bits/stdc++.h>
using namespace std;
char a[24][24];//这个用于存储输入的字符
bool po[30];//26个字母,用以判断此字母是否被读过了
int maxl;//存储最大值
int pa[4] = { 1,0,-1,0 };//控制x走向
int pb[4] = { 0,1,0,-1 };//控制y走向
int n, m;
void f(int x1, int y1, int seft) { //x1代表此时横坐标,y1代表此时纵坐标,seft代表 此时 不同字母的个数
if(seft>maxl) maxl=seft;//如果当前值大过最高值,则更新最高值
int x, y;
for (int i=0; i<=3; i++) {
//有四种方案,分别对应上下左右
x = x1+pa[i];
y = y1+pb[i];
//可行性方案,首先确保移动后的点在方格内,其次,这个点存储的字母未被使用过
if (x>=1 && x<=n && y>=1 && y<=m && (!po[a[x][y]-'A'])) {
po[a[x][y]-'A'] = 1;//标记
f(x, y, seft+1);//递归
po[a[x][y]-'A'] = 0;//解除标记
}
}
}
int main() {
cin>>n>>m;
for (int i=1; i<=n; i++){
for (int j=1; j<=m; j++){
cin>>a[i][j];
}
}
po[a[1][1]-65] = 1;
f(1, 1, 1);//从第一个点开始即可
cout << maxl;
return 0;
}
1213 八皇后问题
【题目描述】在国际象棋棋盘上放置八个皇后,要求每两个皇后之间不能直接吃掉对方。
【输入】(无)
【输出】按给定顺序和格式输出所有八皇后问题的解(见样例)。
【输入样例】(无)
【输出样例】
No. 1
1 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0
0 0 0 0 1 0 0 0
0 0 0 0 0 0 0 1
0 1 0 0 0 0 0 0
0 0 0 1 0 0 0 0
0 0 0 0 0 1 0 0
0 0 1 0 0 0 0 0
No. 2
1 0 0 0 0 0 0 0
0 0 0 0 0 0 1 0
0 0 0 1 0 0 0 0
0 0 0 0 0 1 0 0
0 0 0 0 0 0 0 1
0 1 0 0 0 0 0 0
0 0 0 0 1 0 0 0
0 0 1 0 0 0 0 0
...以下省略
【参考程序】
#include<bits/stdc++.h>
using namespace std;
const int N=1e3;
int a[N],b[N],c[N],d[N],n,cnt=0;
// a[j]=i; //第i行第j列放置
// b[j]=1; //第j列已经放置
// c[i+j]=1; //对角线(右上角到左下角)已放置
// d[i-j+n]=1;//对角线(左上角到右下角)已放置
// 每行/列一个皇后,也就是 第i行/列是第 i个皇后
//放置第 k 个皇后
void dfs(int i){
if(i>n){
cnt++;
cout<<"No. "<<cnt<<endl;
for(int k=1;k<=n; k++) {
for(int j=1; j<=n; j++){
if(a[k]==j) cout<<1<<" ";
else cout<<0<<" ";
}cout<<endl;
}
return ;
}
for(int j=1; j<=n; j++){
if(b[j]==0 && c[i+j]==0 && d[i-j+n]==0){
a[j]=i, b[j]=1, c[i+j]=1, d[i-j+n]=1;
dfs(i+1);
b[j]=0, c[i+j]=0, d[i-j+n]=0;
}
}
}
int main(){
n=8; //cin>>n;
dfs(1);
return 0;
}
1214:八皇后
【题目描述】
会下国际象棋的人都很清楚:皇后可以在横、竖、斜线上不限步数地吃掉其他棋子。
如何将 8 个皇后放在棋盘上(有 8 ×8个方格),使它们谁也不能被吃掉!这就是著名的八皇后问题。
对于某个满足要求的8皇后的摆放方法,定义一个皇后串 a 与之对应,即 a=b1b2...b8,
其中 bi 为相应摆法中第 i 行皇后所处的列数。
已经知道 8 皇后问题一共有 92 组解(即 92 个不同的皇后串)。
给出一个数 b,要求输出第 b 个串。
串的比较是这样的:皇后串 x 置于皇后串 y 之前,当且仅当将 x 视为整数时比 y 小。
【输入】第 1 行是测试数据的组数n,后面跟着n行输入。
每组测试数据占1行,包括一个正整数b(1≤b≤92)。
【输出】输出有n行,每行输出对应一个输入。输出应是一个正整数,是对应于b的皇后串。
【输入样例】
2
1
92
【输出样例】
15863724
84136275
【参考程序】
#include<bits/stdc++.h>
using namespace std;
const int N=1e3;
int a[N],b[N],c[N],d[N], n, cnt=0;
int data[N][N];
// a[j]=i; //第i行第j列放置
// b[j]=1; //第j列已经放置
// c[i+j]=1; //对角线(右上角到左下角)已放置
// d[i-j+n]=1;//对角线(左上角到右下角)已放置
// 每行/列一个皇后,也就是 第i行/列是第 i个皇后
//每一列一个皇后
void dfs(int j){
if(j>n){
cnt++;
for(int i=1; i<=n; i++){
data[cnt][i]=a[i];
}
}
for(int i=1; i<=n; i++){
if(b[i]==0 && c[i+j]==0 &&d[i-j+n]==0){
a[j]=i, b[i]=1, c[i+j]=1, d[i-j+n]=1;
dfs(j+1);
b[i]=0, c[i+j]=0, d[i-j+n]=0;
}
}
}
int main(){
n=8;
dfs(1);
int num; cin>>num;
for(int i=1; i<=num; i++){
int b; cin>>b;
for(int j=1; j<=n; j++){
cout<<data[b][j];
}cout<<endl;
}
return 0;
}
1215 迷宫
1216 红与黑
1217 棋盘问题
1218 取石子游戏
1219 马走日
【参考程序】
#include<iostream>
#include<cstring>
using namespace std;
const int N=10;
int a[N][N];
int dis[][2]={-2,-1, -1,-2, 1,-2, 2,-1, 2,1, 1,2, -1,2, -2,1};
int n,m,sx,sy,ans;
void pr(){
for(int i=0; i<n; i++){
for(int j=0; j<m; j++){
cout<<a[i][j]<<" ";
}cout<<endl;
}cout<<endl;
}
//遍历数量,将要遍历的点
void dfs(int cnt,int x,int y){
// if(cnt<4) pr();
if(cnt==n*m) {
ans++; return;
}
for(int i=0; i<8; i++){
int tx=x+dis[i][0];
int ty=y+dis[i][1];
if(tx<0||tx>=n||ty<0||ty>=m) continue;
if(a[tx][ty]==0){
a[tx][ty]=1;
dfs(cnt+1,tx,ty);
a[tx][ty]=0;
}
}
}
int main(){
// freopen("data.in", "r", stdin);
int t; cin>>t;
while(t--){
cin>>n>>m>>sx>>sy;
ans=0;
memset(a, 0, sizeof(a));
a[sx][sy]=1;
dfs(1,sx,sy);
cout<<ans<<endl;
}
return 0;
}

浙公网安备 33010602011771号