1.看了15道搜索的题。
有2道题看题解看不会:树不会,有点能理解,但是代码实现看不懂;拓扑序看不懂。
2.搜索可以和 分治思想, 暴力枚举, 数据结构 数学 知识结合起来。
技巧:1.可以反向递归:比如在选和不选这两个状态,我们可以全不选,一直搜到底,然后从后面开始进行选和不选的递归。
P1123 取数游戏
#include<bits/stdc++.h>//万能头文件
using namespace std;
const int d[8][2]={1,0,-1,0,0,1,0,-1,1,1,-1,1,1,-1,-1,-1};//方向数组用来控制搜索时的方向
int t,n,m,s[8][8],mark[8][8],ans,mx;
void dfs(int x,int y){//搜索函数,表示搜索点(x,y)
if(y==m+1){//当y到边界时,搜索下一行
dfs(x+1,1);
return;
}
if(x==n+1){//当x到边界时,搜索结束,刷新最大值
mx=max(ans,mx);
return;
}
dfs(x,y+1);// 不取此数的情况
if(mark[x][y]==0){ //取此数的情况(需保证此数周围没有取其他数,即mark[i][j]==0)
ans+=s[x][y];
for(int fx=0;fx<8;++fx){ //标记周围的数
++mark[x+d[fx][0]][y+d[fx][1]];
}
dfs(x,y+1);
for(int fx=0;fx<8;++fx){ //回溯
--mark[x+d[fx][0]][y+d[fx][1]];
}
ans-=s[x][y];
}
}
int main(){
cin>>t;
while(t--){
memset(s,0,sizeof(s));
memset(mark,0,sizeof(mark));//在做每个数据前都要初始化数组
cin>>n>>m;
for(int i=1;i<=n;++i){
for(int j=1;j<=m;++j){
cin>>s[i][j];
}
}
mx=0;
dfs(1,1);//从点(1,1)开始搜索
printf("%d\n",mx);//输出答案
}
return 0;
}
转自:题解 P1123 【取数游戏】 - 绿萧 的博客 - 洛谷博客https://www.luogu.com.cn/blog/greenxiao/solution-p1123
也可以从下面递归,反向思维。P1036 选数
#include<bits/stdc++.h>
using namespace std;
char pic[2048][2048];//建图
void huatu(int x,int y,int jie)
{
if(jie==1)//从底层建起
{
pic[x][y]='/';
pic[x][y+1]='_';
pic[x][y+2]='_';
pic[x][y+3]='\\';
pic[x-1][y+1]='/';
pic[x-1][y+2]='\\';//'\\'里有一是个转义符号
}
else//不断递归,分成更小的
{
int m=pow(2,jie);这里每个三角形的高度随阶次减少
huatu(x,y,jie-1);//降阶
huatu(x,y+m,jie-1);//分图,每次都相当于在底下画两个一样的,这里因为是从下往上,不然不好存————构造的第2步
huatu(x-m/2,y+m/2,jie-1);//画一个倒着的在三个之间————构造的第3步,反着画一个,相当于去掉中间的
}
}
int main()
{
int n;
cin>>n;
memset(pic,' ',sizeof(pic));//全图空格
int m=pow(2,n);//图的层数为2的n次方
huatu(m,1,n);//开始画图 从下往上
int a,b;
for(a=1;a<=m;a++)
{
for(b=1;b<=m*2;b++)
printf("%c",pic[a][b]);
printf("\n");
} //输出
printf("\n");
return 0;
}
题解 P1498 【南蛮图腾】 - DDF - 洛谷博客https://www.luogu.com.cn/blog/alvais7heosfather/solution-p1498
2.连通块问题:将字符转换成整形变量。用个同样大的二维数组表示是否满足条件或者用另一个状态覆盖。
3.避免出现111这种用个数组判断是否选过,321 123 213 这样用不降序原则:下次递归的时候第一个选择的数组加1.
4.图形问题找找画图的规律,找不到就试试能不能暴力刻画,不行放弃。
5.数学有关的题,从数据里面找宏观的规律,往大里想。动手写几个例子,算一算。
6.可以从多个点进行搜索。
7.有些时候用数组判断周围有没有选过数时,可以用++;
8.不知道怎么说,就是在有向图中问有几个点时可以从其他每个点都可以到达的。我们可以用搜索从每个点遍历每个点,看最后有几个点的数组等于总点数。
9.有向图求所有路径;从第一个点开始往后减节点,每减去一个节点,该节点后面链接的点就加1,最后一个点的值就是总路径。
3.可以处理 二维平面有关的问题:染色问题地图问题,连通块问题(P1506 拯救oibh总部,从四周可以搜索)。 与选数有关的问题: 多个数的全排列, 矩阵找元素和最大,有条件的选数、如P1460 健康的荷斯坦奶牛 Healthy Holsteins,和课件上的选书,工作效率。 有向图问题:(有向图一般用二维的vector表示)。找路径的条数。
上面除了多个数的全排列和有条件的选数,全是新题型。
4.感想有两个方面:
一是对深搜的感想:
搞明白什么时候用深搜:数据范围较小,变化的状态
深搜的每个状态是什么,怎么表达:1.有个奶牛钻洞的题,有个题解是将字符换'X''O'排列顺序成二进制'1''0',用排列的’1‘’0‘ 二进制转化成的十进制整数作为状态。 2.联通块问题:每个状态就是点是不是。
深搜每个状态要怎么选择,怎么走下去,怎么递归下去:1.P1036 选数,判断子集和是不是素数,刚开始我思维固化了,用了个数组去判断子集元素有没有选,结果选重复了。避免出现111时,可以用一个数组判断时候在一次整个的选择中选过; 避免123 321 213这种时,可以递归的时候升一下选择的开始数值。2.在选数问题,选和不选可以用加一,或者不变来进行递归:dfs(t+1,i+1)和dfs(t,i+1);3.连通块问题:依照题目向周围方向延申搜索。
深搜的终止条件是什么:
深搜剪枝的例子有哪些,啥时候剪枝:1.选数问题:当剩下的所有数都加上或减去时,还不满足条件时可以剪去。2.二维平面问题:当选择的点超过范围的时候剪枝。3.有向图,求所有路径的时候,可以用一个数组在每个点存上该点往后的所有路径。
P4017 最大食物链计数
#include<bits/stdc++.h>
using namespace std;
const int maxn=1000001;
const int mod=80112002;
queue<int> q;
int d[maxn],in[maxn],eat[maxn],head[maxn];
int n,m,tot,ans;
struct E{
int to,nxt;
}e[maxn];
inline void add(int u,int v)
{
e[++tot].to=v;
e[tot].nxt=head[u];
head[u]=tot;
}
inline void topo()
{
for(int i=1;i<=n;i++)
{
if(in[i]==0)
{
q.push(i);
d[i]++;//入度为0即为最弱的一个点,所以d[i]=1;
}
}
while(!q.empty())
{
int p=q.front();
q.pop();
for(int i=head[p];i;i=e[i].nxt)
{
int go=e[i].to;
d[go]=(d[go]+d[p])%mod;//这句话是本题的核心
in[go]--;
if(in[go]==0)
q.push(go);
}
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=m;i++)
{
int a,b;
cin>>a>>b;
add(a,b);
in[b]++;
eat[a]++;//这个是判断最猛的动物没人敢吃所以它的eat即为0,在下面有用。
}
topo();
for(int i=1;i<=n;i++)
{
if(eat[i]==0)
ans=(ans+d[i])%mod;
}
cout<<ans;
}
同一题可能有不同的搜索递归方式,要明确用什么递归,用什么表示状态,用什么表示结束,要不要回溯。
二是对学习状态的:感觉要干一件事,就要干一件事。
每次总结的时候才发现这周没怎么学东西,而且还忘了很多,或许当时有灵光一闪,但是现在已经忘了。