搜索几个小题讲解

文章介绍了四道编程题目,包括全排列问题的实现,使用C++的Nextprime函数进行排列生成;二叉树问题,通过广度优先搜索找出安全路径的最大数量;自然数拆分问题,递归方法生成所有可能的拆分序列;以及判断是否能用木棒组成正方形的问题,通过深度优先搜索解决。每道题都提供了题解思路和代码示例。
摘要由CSDN通过智能技术生成

搜索题解

题目

洛谷—P1706 全排列问题(题目链接:https://www.luogu.com.cn/problem/P1706)。

全排列问题

题目描述

按照字典序输出自然数 1 1 1 n n n 所有不重复的排列,即 n n n 的全排列,要求所产生的任一数字序列中不允许出现重复的数字。

输入格式

一个整数 n n n

输出格式

1 ∼ n 1 \sim n 1n 组成的所有不重复的数字序列,每行一个序列。

每个数字保留 5 5 5 个场宽。

样例 #1

样例输入 #1

3

样例输出 #1

1    2    3
    1    3    2
    2    1    3
    2    3    1
    3    1    2
    3    2    1

提示

1 ≤ n ≤ 9 1 \leq n \leq 9 1n9

接下来是题解极其代码

题解:
题目的意思其实很简单;就是首先输入一个数,然后接下来列举出所有的排列组合。并且格式化输出。
格式化输入输出这里就要引用一个头文件iomanip,这个头文件是格式化输入输出的文件也就是下面setw(5)啦。setw(5)的意思就是 设置字段宽度为5位.
全排有很多种解法这里只是其中一种。

代码如下:

#include<iostream>
#include<iomanip>// 这里调用的是setw(5)的头文件
using namespace std;
int b[10];
//这个部分就是如何进行全排
//主要思想很简单就是去找每个数的位置,一个数先去找到他所有的位置,接下来在这个数找到每一个位置的时候,下面的数也依次去找位置就是找到一个就下一个找,直到第一个成为最后一个数。
int  Nextprime(int a[],int len)//长度就是输入的N
{
    int i=0;
    int j=len-2,k=len-1;//初始化比较的位置
    int r=len-1,s=0;
    while(a[j]>a[j+1])//比较两个数的大小,如果a[j]大于a[j+1]就是这个位置没有用过,定位在这里    {
        j--;
        if (j==-1) return 0;
    }
    while (a[j]>a[k]) k--;//这个是判断顺序的
    swap(a[j],a[k]);//交换位置
    s=j+1;
    while (r>s)
    {
        swap(a[r],a[s]);//交换位置
        r--;
        s++;
    }
    return 1;
}
//格式化输出
void print(int a[],int len)
{
    for (int i=0;i<len;i++)
    {
        cout<<setw(5)<<a[i];
    }
}
int main()
{
    int n;
    cin>>n;
    for (int i=0;i<n;i++)
    {
        b[i]=i+1;
    }//得到要排列的数
    print(b,n);//这里先输出正常顺序,
    cout<<endl;//输出一个换行
    while (Nextprime(b,n))//因为Nextprime(b,n)是一个有返回值的函数,返回值是1,利用这个1进行循环。
    {
        print(b,n);
        cout<<endl;
    }
    return 0;
}

第二题

题目链接

入门

题目描述

不是任何人都可以进入桃花岛的,黄药师最讨厌像郭靖一样呆头呆脑的人。所以,他在桃花岛的唯一入口处修了一条小路,这条小路全部用正方形瓷砖铺设而成。有的瓷砖可以踩,我们认为是安全的,而有的瓷砖一踩上去就会有喷出要命的毒气,那你就死翘翘了,我们认为是不安全的。你只能从一块安全的瓷砖上走到与他相邻的四块瓷砖中的任何一个上,但它也必须是安全的才行。

由于你是黄蓉的朋友,她事先告诉你哪些砖是安全的、哪些砖是不安全的,并且她会指引你飞到第一块砖上(第一块砖可能在任意安全位置),现在她告诉你进入桃花岛的秘密就是:如果你能走过最多的瓷砖并且没有死,那么桃花岛的大门就会自动打开了,你就可以从当前位置直接飞进大门了。

注意:瓷砖可以重复走过,但不能重复计数。

输入格式

第一行两个正整数 W W W H H H,分别表示小路的宽度和长度。

以下 H H H 行为一个 H × W H\times W H×W 的字符矩阵。每一个字符代表一块瓷砖。其中,. 代表安全的砖,# 代表不安全的砖,@ 代表第一块砖。

输出格式

输出一行,只包括一个数,即你从第一块砖开始所能安全走过的最多的砖块个数(包括第一块砖)。

样例 #1

样例输入 #1

11 9
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
...........

样例输出 #1

59

提示

数据规模与约定

对于全部的测试点,保证 1 ≤ W , H ≤ 20 1 \leq W,H\le 20 1W,H20

题解

开个bool二维数组visited记录每个点有没有走到过

从队列取点出来的时候如果已经是visited就直接跳过,否则cnt++

然后把四周的点还没有visited的入队

代码如下

#include <iostream>
#include <cmath>
#include <string>
#include <queue>
#define maxn 25
using namespace std;
int w,h;
int fx,fy;
bool map[maxn][maxn];
bool visited[maxn][maxn];//标记map
int ans=0;
typedef pair<int,int> point;
int main()
{
    cin>>w>>h;
    string str;
    for(int i=1;i<=h;++i)
    {
        cin>>str;
        for(int j=1;j<=w;++j)
        {
            if(str[j-1]=='.')
                map[i][j]=true;
            else if(str[j-1]=='@')
            {
                fx=i; fy=j;
            }
        }
    }
    queue<point> q;
    q.push(make_pair(fx,fy));
    while(!q.empty())
    {
        point p=q.front();
        q.pop();
        int x=p.first, y=p.second;
        if(visited[x][y]) continue;
        visited[x][y]=true;
        ans++;
        if(x-1>=0 && !visited[x-1][y] && map[x-1][y]) q.push(make_pair(x-1,y));
        if(y-1>=0 && !visited[x][y-1] && map[x][y-1]) q.push(make_pair(x,y-1));
        if(y+1<=w && !visited[x][y+1] && map[x][y+1]) q.push(make_pair(x,y+1));
        if(x+1<=h && !visited[x+1][y] && map[x+1][y]) q.push(make_pair(x+1,y));
    }
    cout<<ans<<endl;
    return 0;
}

题目三

自然数的拆分问题

题目描述

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

输入格式

输入:待拆分的自然数 n n n

输出格式

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

样例 #1

样例输入 #1

7

样例输出 #1

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 ≤ n ≤ 8 1\le n\le 8 1n8

题解如下
就是统计方案;然后我们要知道这个数是可以用哪几种数来得到
也就是说,2我们可以由1+1 的得到;3我们可以用1+1+1得到还有1+2 得到。

代码如下

#include<iostream>
#include<cstdio>
#include<cstdlib>
using namespace std;
int a[10001]= {1},n,total;
void print(int t) {
    for(int i=1; i<=t-1; i++)//输出一种方案解
        cout<<a[i]<<'+';
    cout<<a[t]<<endl;
    total++;//方案数加一
}
void search(int s,int t) {
    for(int i=a[t-1]; i<=s; i++)
        if(i<n) {//当前数 i 要大于等于前 1 位数,且不超过 n
            a[t]=i;//保存当前拆分数 i
            s-=i;// s 减去数 i,s 的值继续拆分
            if(s==0) print(t);//当 s=0 时,拆分结束输出结果
            else search(s,t+1);//当 s>0 时,继续递归
            s+=i;//回溯:加上拆分的数,以便产生所有可能的拆分
        }
}
int main() {
    cin>>n;
    search(n,1);//将要拆分的数 n 传递给 s
    return 0;
}

题目四

狗哥玩木棒

题目背景

狗哥又趁着语文课干些无聊的事了…

题目描述

现给出一些木棒长度,那么狗哥能否用给出的木棒(木棒全用完)组成一个正方形呢?

输入格式

输入文件中的第一行是一个整数 n n n 表示测试的组数,接下来 n n n 行表示每组的测试数据。 每行的第一个数为 m ( 4 ≤ m ≤ 20 ) m(4\leq m\leq 20) m(4m20),接下来 m m m 个数 a i ( 1 ≤ a i ≤ 1000 ) a_i(1\leq a_i\le 1000) ai(1ai1000) 表示木棒的长度。

输出格式

对于每组测试数据,如果可以组成正方形输出“yes”,否则输出“no”。

样例 #1

样例输入 #1

3
4 1 1 1 1 
5 10 20 30 40 50 
8 1 7 2 6 4 4 3 5

样例输出 #1

yes
no
yes

提示

狗哥快抓狂了

题解思路: 1、对于每组数据,取它们的和,如果对4取模不等于0(也就是不能组成正方形),直接输出“no”; 2、搜索中,对于每种情况特殊处理。

代码如下
#include <iostream>
#include <cstring>
using namespace std;
int s;
int m;
int n;
bool vis[10001];
int a[10001];
bool flag;
void dfs(int ans,int sum){//sum是目前已经组成了的边数,ans则是目前第sum+1条边组成的情况(即边的长度)
	if(flag){//如果这组数据已经可以组成一个正方形了,自然不用继续搜索
		return;
	}
	if(ans>s){//当前边的长度超过了正方形边长,即这种解法不能组成正方形的一边
		return;
	}
	if(ans==s){
		dfs(0,sum+1);//可以组成正方形的一边,开始搜索下一条边的组成方案
	}
	if(sum==4){//正方形的四条边都好了,标记,退出
		flag=1;
		return;
	}
	for(int i=1;i<=n;i++){
		if(!vis[i]){
			vis[i]=1;
			dfs(ans+a[i],sum);//没什么好解释了,dfs模板
			vis[i]=0;
		}
	}
}
int main(){
	cin>>m;
	while(m--){
		s=0;
		flag=0;
		memset(a,0,sizeof(0));
		cin>>n;
		for(int i=1;i<=n;i++){
			cin>>a[i];
			s+=a[i];
		}
		if(s%4!=0){
			cout<<"no"<<endl;
			continue;
		}
		s/=4;
		dfs(0,0);
		if(flag){
			cout<<"yes"<<endl;
		}
		else{
			cout<<"no"<<endl;
		}
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值