2021-01-20 总结

2021-01-20 总结

今天的课收获颇多,对于我这么一个搜索学的一知半解的人来说,这样一节搜索的强化课对我实在是太有帮助了,虽然现在离精通还有很远,但是至少我已经能独立写出一些思维难度比较简单的搜索题了。总之,今天真是收获满满。
1.八皇后

题目描述 一个如下的 6×6的跳棋棋盘,有六个棋子被放置在棋盘上,使得每行、每列有且只有一个,每条对角线(包括两条主对角线的所有平行线)上至多有一个棋子。

上面的布局可以用序列 2 4 6 1 3 5 来描述,第 i 个数字表示在第 i 行的相应位置有一个棋子,如下: 行号 1 2 3 4 5 6
列号 2 4 6 1 3 5

这只是棋子放置的一个解。请编一个程序找出所有棋子放置的解。 并把它们以上面的序列方法输出,解按字典顺序排列。 请输出前 3个解。最后一行是解的总个数。

输入格式
一行一个正整数 n,表示棋盘是 n×n 大小的。

输出格式
前三行为前三个解,每个解的两个数字之间用一个空格隔开。第四行只有一个数字,表示解的总数。

输入输出样例
输入
6
输出
2 4 6 1 3 5
3 6 2 5 1 4
4 1 5 2 6 3
4

八皇后的原题我在网上没找到,只好拿 n 皇后的题来凑合了,也以这道题为例谈一谈搜索。
这道题因为皇后可以吃掉八个方向上的棋子,也就意味着皇后的八个方向上是不能再放皇后的,所以我的思路就是每放一个皇后,就把这个皇后八个方向上的所有位置都打上标记,下一次再放皇后时,就找没有打上标记的地方放。
接下来是代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int a[20],b[20],c[20],d[20];
int ans;
void print(){
    
    
	for(int i=1;i<=8;i++){
    
    
		printf("%d ",a[i]);
	}
	printf("\n");
	ans++;//累加器,用于记录解的个数;
} 
void cwy(int i){
    
    
	if(i>8){
    
    //边界条件的判断; 
		print();
		return;
	}
	for(int j=1;j<=8;j++){
    
    
		if(b[j]==0&&c[i+j]==0&&d[i-j+8]==0){
    
    //条件判断;
			a[i]=j;b[j]=1;c[i+j]=1;d[i+8-j]=1;//给不能放皇后的地方打上标记; 
			cwy(i+1);
			b[j]=0;c[i+j]=0;d[i-j+8]=0;//回溯,去掉标记; 
		}
	}
}
int main()
{
    
    
	cwy(1);
	printf("**%d**",ans);
	return 0;
}

至于 n 皇后,实际上和八皇后一模一样,只是把 8 都改成了 n 。。。
不过为了练搜索,我还是又写了一遍。

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,ans;
int a[100001],b[100001],c[100001],d[100001];
void print()
{
    
    
	if(ans<=2){
    
    
		for(int i=1;i<=n;i++){
    
    
			printf("%d ",a[i]);
		}
		printf("\n");
	}
	
	ans++;
}
void cwy(int i)
{
    
    
	if(i>n){
    
    
		print();
		return;
	}
	for(int j=1;j<=n;j++){
    
    
		if(b[j]==0&&c[j+i]==0&&d[i-j+n]==0){
    
    
			b[j]=1;c[j+i]=1;d[i-j+n]=1;
			a[i]=j;
			cwy(i+1);
			b[j]=0;c[j+i]=0;d[i-j+n]=0;
		}
	}
}
int main()
{
    
    
	scanf("%d",&n);
	cwy(1);
	printf("%d",ans);
	return 0;
}

2.自然数的拆分问题

题目描述
任何一个大于1的自然数n,总可以拆分成若干个小于n的自然数之和。现在给你一个自然数n,要求你求出n的拆分成一些数字的和。每个拆分后的序列中的数字从小到大排序。然后你需要输出这些序列,其中字典序小的序列需要优先输出。

输入格式
输入:待拆分的自然数n。

输出格式
输出:若干数的加法式子。

输入输出样例
输入
7
输出
1+1+1+1+1+1+1
1+1+1+1+1+2
1+1+1+1+3
1+1+1+2+2
1+1+1+4
1+1+2+3
1+1+5
1+2+2+2
1+2+4
1+3+3
1+6
2+2+3
2+5
3+4

这道自然数的拆分问题,总结起来有两个要点,一是每个拆分的序列中,数字需要按从小到大的顺序输出;二是序列要按照字典序的顺序输出。第二点比较好解决,搜索中的循环从 1 开始即可,第一点可以通过每次循环存一下这层循环所取的值,并在下一层循环中从这一个值开始循环(由于本题本身就需要存每一层循环所取的值,所以顺带就把这项工作做了),这样就可以保证下一次循环中所取的值不小于这一层循环所取的值,进而使数字按从小到大的顺序输出。另外,如果把第二点做好,就不用考虑输出重复的问题了。
代码:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
int n,a[10001]={
    
    1},sum,y,vis[10001];
void print(int);
void cwy(int x,int y){
    
    
//x代表还有多少数可以拆;
//y代表层数;
	if(x==0){
    
    
		print(y);
	}
	for(int i=a[y-1]/*从上一次存入的值开始这一次循环,保证数字按照从小到大的顺序输出;;*/;i<=x&&i<n;i++){
    
    
		a[y]=i;//存当前循环所取的值;
		x-=i;
		cwy(x,y+1);
		x+=i;//回溯;
	}
}
void print(int y){
    
    
	printf("%d=",n);
	for(int i=1;i<=y-2;i++){
    
    
		printf("%d+",a[i]);
	}
	printf("%d\n",a[y-1]);
}
int main()
{
    
    
	scanf("%d",&n);
	cwy(n,1);
	return 0;
}

今天就写到这,有关汉诺塔的问题明天再写,我得去狂肝作业了。

猜你喜欢

转载自blog.csdn.net/chenweiye1/article/details/112907434