在C++的各种题目中,搜索题绝对占了一大部分。这个系列将由易到难,一步步讲解C++中关于搜索的各种运用(顺便帮作者也准备一下今年的CSP)。
那么话不多说,我们开始做题吧。
题目传送门: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;
}
题目传送门:全排列问题 - 洛谷
一看题目,我们就想到了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;
}
题目传送门:[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;
}
那么这篇文章的内容就到这里了,最后,如果你觉得这篇文章还不错的话,那就点个关注点个赞。这是免费的,你随时可以取消。你的支持是作者最大的动力,感谢支持!
(顺带一提,今天是作者生日,看在作者那么努力的份上点个关注叭,球球辣)