【C++搜索练习】第一周 深搜/递归的基本运用

在C++的各种题目中,搜索题绝对占了一大部分。这个系列将由易到难,一步步讲解C++中关于搜索的各种运用(顺便帮作者也准备一下今年的CSP)。

那么话不多说,我们开始做题吧。

A.记忆化搜索-function

题目传送门:Function - 洛谷

很简单的一道模拟搜索题,w函数按照题意就这么写:

int w(int a, int b, int c){
    if(a <= 0 || b <= 0 || c <= 0) return 1;
    if(a > 20 || b > 20 || c > 20) return w(20, 20, 20);
    if(a < b && b < c) return w(a,b,c-1)+w(a,b-1,c-1)-w(a,b-1,c);
    return w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1,b-1,c-1);
}

但就像题目说的,这种代码肯定过不了(这么多递归不TLE也要RE)

怎么办呢?

我们发现,在运行的过程中,有很多时候同一组参数会被反复计算。

比如,当多组输入中a,b,c均大于20时,w(20,20,20)就会计算非~~~常多次。

所以,我们可以加入一个名为f的三维数组,用f[i][j][k]存储w(i, j, k)的值。在计算w函数时,如果f[a][b][c]已经算过,就直接返回值,否则再计算并存储。

完整代码如下:

#include
using namespace std;
#define int long long 
//比赛场上WA一场,不开longlong见祖宗

int f[114][114][114];
//实际上开到25就够

int w(int a, int b, int c){
    if(a <= 0 || b <= 0 || c <= 0) return 1;
    //负数特判
    if(a > 20 || b > 20 || c > 20){
        f[20][20][20] = w(20, 20, 20);
        return w(20, 20, 20);
    }//注意要先判断是否大于20,否则很可能越界(注意数据范围)
    if(f[a][b][c] != -1) return f[a][b][c];
    if(a < b && b < c){
        f[a][b][c] = w(a,b,c-1)+w(a,b-1,c-1)-w(a,b-1,c);
    }else f[a][b][c] = w(a-1,b,c)+w(a-1,b-1,c)+w(a-1,b,c-1)-w(a-1,b-1,c-1);
    return f[a][b][c];
    //正常模拟,储存数据
}

int a, b, c;

signed main()
{
    memset(f, -1, sizeof(f));
    //设置初始值
    while(1){
        cin >> a >> b >> c;
        if(a == -1 && b == -1 && c == -1) break;
        //全是-1就跳出
        else printf("w(%lld, %lld, %lld) = %lld\n", a, b, c, w(a, b, c));
        //         注意%lld不要写成%d
    }
    return 0;
}

B.回溯-全排列问题

题目传送门:全排列问题 - 洛谷

一看题目,我们就想到了next_permutation() 深搜:首先搜索开头为1的排列。

由于全排列中1-n各数都只出现一次,所以如果要搜索的数已经出现过则不用再搜。

而搜索过一个数之后,要把已经用过的标记取消,这就是回溯。

如果你还不理解,那就看代码:

#include
#include
using namespace std;
#define For(i, j, k) for(int i = j; i <= k; i++)
#define dFor(i, j, k)for(int i = j; i >= k; i--)
//for循环缩写,个人习惯

int n;
int a[15], vis[15];
//a[i]表示当前枚举的组合,vis标记某个点是否用过

void print(){      //找到结果后打印
    For(i, 1, n) printf("%5d", a[i]);
    //保持5格常距输出
    cout << endl;   //别忘了换行
}

void f(int x){
    if(x == n + 1){     //搜完了
        print();        //打印
        return ;
    }
    For(i, 1, n){   //枚举下一个数
        if(!vis[i]){//如果没有用过
            vis[i] = 1; //标记
            a[x] = i;   //加入到数组中
            f(x+1);     //搜索
            vis[i] = 0; //回溯:搜完之后重新标记为0
        }
    }
}

int main()
{
    cin >> n;
    f(1);
    return 0;
}

 C.求先序排列

题目传送门:​​​​​​​[NOIP2001 普及组] 求先序排列 - 洛谷

这是一道关于二叉树的题。先不管怎么写程序,我们想想,如果用人脑,我们会怎么解决?

例如样例中的中序BADC,后序DBCA:

首先,可以根据后续遍历分析出A是根;在中序遍历中,可知B为左子树,DC为右子树。

然后,我们先输出A,再分别分析左子树和右子树......

想到这里,代码就呼之欲出了:

#include
using namespace std;

void f(string a, string b){    //中序a,后序b,求先序
    if(a.size()>0){
        char ch = b[b.size()-1];
        //找到根
        cout << ch;
        int k = a.find(ch);
        //算出根的位置
        f(a.substr(0, k), b.substr(0, k));          //遍历左子树
        f(a.substr(k+1), b.substr(k, a.size()-k-1));//遍历右子树
    }
}

int main()
{
	string a, b;
	cin >> a >> b;
	f(a, b);
	return 0;
}

那么这篇文章的内容就到这里了,最后,如果你觉得这篇文章还不错的话,那就点个关注点个赞。这是免费的,你随时可以取消。你的支持是作者最大的动力,感谢支持!

(顺带一提,今天是作者生日,看在作者那么努力的份上点个关注叭,球球辣)

你可能感兴趣的:(C++,算法练习,c++,开发语言,深度优先,算法)