搜索专题总结

来源:互联网 发布:pc机安装mac os x 编辑:程序博客网 时间:2024/05/26 07:29

经典例题随时可能更新…
其实这是不可能的,因为博主太懒了


迭代加深搜索

特点

描述:
人为加一个最大深度限定,再进行dfs,若在当前深度搜到了答案就输出,否则将最大深度增大。
优点:
①当题目所对应的搜索树是一棵无限树时(如经典例题1)。尽管在搜索过程中,有重复计算,但由于搜索树一般呈指数级增长,所以相对来说重复的计算量并不太大;
②当题目搜索的状态难以保存,或者空间复杂度较高时(如经典例题2)。它在搜索的实现上是dfs,结合其空间复杂度小和回溯的优点,就可以更好地处理问题。

经典例题

1.埃及分数

[Description]
在古埃及,人们使用单位分数的和(形如1/a的, a是自然数)表示一切有理数。 如:2/3=1/2+1/6,但不允许2/3=1/3+1/3,因为加数中有相同的。 对于一个分数a/b,表示方法有很多种,但是哪种最好呢? 首先,加数少的比加数多的好,其次,加数个数相同的,最小的分数越大越 好。 如: 19/45=1/3 + 1/12 + 1/180 19/45=1/3 + 1/15 + 1/45 19/45=1/3 + 1/18 + 1/30, 19/45=1/4 + 1/6 + 1/180 19/45=1/5 + 1/6 + 1/18. 最好的是最后一种,因为1/18比1/180,1/45,1/30,1/180都大。
[Input Description]
a b
[Output Description]
若干个数,自小到大排列,依次是单位分数的分母。
[Sample Input]
19 45
[Sample Output]
5 6 18
思路
用迭代加深算法,每次再次调用dfs时,必须传递剩余分数的分子和分母,也可以顺便约分一下。另外在处理的时候,要计算一下分母的上界和下界即可。
代码

#include <iostream>#include <cstring>#include <cstdio>using namespace std;typedef long long lld;const int len=101;lld ans[len],t[len],depth;bool flag=false; lld max(lld a,lld b){return a>b?a:b;}lld min(lld a,lld b){return a<b?a:b;}lld gcd(lld a,lld b){return b?gcd(b,a%b):a;}void dfs(lld a,lld b,lld k){    if(k==depth+1||a<0)      return ;    if(b%a==0&&b/a>t[k-1])    {        t[k]=b/a;        if(!flag||t[k]<ans[k])//若没找到解,或找到的解的最小分数更大,即其分母更小         {            memcpy(ans,t,sizeof(t));            flag=true;        }        return ;    }//下一个分数<当前分数,下一个分数分母大于上一个     lld s=max(b/a,t[k-1]+1);    lld e=(depth-k+1)*b/a;    if(flag&&e>=ans[depth])      e=ans[depth]-1;    for(lld j=s;j<=e;j++)//枚举下一个分数的分母     {        s=s;        t[k]=j;        lld m=gcd(b,j);        dfs((a*j-b)/m,b*j/m,k+1);        t[k]-=j;    }}int main(){    lld n,m;    scanf("%d %d",&n,&m);    t[0]=1;    depth=0;    while(!flag)    {        depth++;        dfs(n,m,1);    }    for(int i=1;i<=depth;i++)      printf("%d ",ans[i]);    return 0;}

2.Tempter of the Bone II (HDU2128)

详细戳此


Astar及IDA*

特点

描述:
即在原搜索算法的基础上加上一个估价函数,对扩展出的节点估价,以此进行剪枝。
优点:
对当前状态估价,预先估计当前节点可以扩展出ans的可能性,再取最有可能的进行扩展。是一种比较高级的剪枝方法,能减去很多不必要的计算量。值得注意的是,有时候估价函数需要写得很精细,比如一些求最短路的题目,才能保证ans一定是最优;而有时候估价函数要写得粗糙一些(如经典例题3),才能更有效地减小估价的时间复杂度。

经典例题

3.Biggest Number (UVa11882)(A*)

详细戳此

4.Editing a Book (UVa 11212)(IDA*)

详细戳此


双向广搜

特点

描述
对bfs算法的一种改进。将起点和终点同时加入两个广搜队列中,每次对两个队列搜索一层,并进行标记。当在某种状态发生重合时,即找到了解。
优点
可以大大地减少搜索的状态数以及时间。因为搜索的状态数一般是随着层数指数级增长的,单向bfs需要搜ans层,双向bfs则只需搜两个大概ans/2层。

经典例题

5.Nightmare II (HDU3085)

题目大意
给你一张地图,描述了不能走的地方(然而ghost却可以走),ghost,M和G的位置。每个ghost每秒分裂占领所有离它两格距离的格子,M每秒移动3格,G每秒移动1格。询问M和G能否在ghost碰到他们之前(刚好相遇时碰到也不行)相遇,能则输出最短用时,不能输出-1。
思路
由于ghost是不受地形限制的,那么可以直接以曼哈顿距离来判断t时某格是否被占领,因此,我们就只需要考虑M和G的移动方案了,然后它就成了一道很裸的双向bfs题。
值得注意的是,为了使得我们每次操作只搜完一层,可以在开始搜之前将队列的元素个数保存一下,具体见代码部分。
代码

#include <iostream>#include <cstring>#include <cstdio>#include <queue>using namespace std;template <typename Tp> void read(Tp &x){    x=0;    char ch=getchar();    while(ch<'0'||ch>'9') ch=getchar();    while(ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();}const int size=810;int v[4][2]={{-1,0},{0,1},{1,0},{0,-1}};int z,n,m,mx,my,gx,gy,zx1,zy1,zx2,zy2,s;char map[size][size];bool flag,vis[size][size][2];queue<int> q[2];int abs(int x){return x>0?x:-x;}bool check(int x,int y,int s)//判合法性有点复杂,于是就写在外面了{    if(x<1||x>n||y<1||y>m||map[x][y]=='X')      return false;    if(abs(x-zx1)+abs(y-zy1)<=2*s)      return false;    if(abs(x-zx2)+abs(y-zy2)<=2*s)      return false;    return true;}bool bfs(int k,int s){    int x,y,xx,yy,num=q[k].size();    while(num--)//每次只搜一层    {        x=q[k].front()>>10;        y=(x<<10)^q[k].front();        q[k].pop();        if(!check(x,y,s))//ghost先走          continue;        for(int i=0;i<4;i++)        {            xx=x+v[i][0],yy=y+v[i][1];            if((!check(xx,yy,s))||vis[xx][yy][k])              continue;            if(vis[xx][yy][k^1])//判是否被另一个队列标记过              return true;            vis[xx][yy][k]=true;            q[k].push(xx<<10|yy);        }    }    return false;}int main(){    read(z);    while(z--)    {        read(n),read(m);        flag=false;        zx1=-1;        for(int i=1;i<=n;i++,getchar())          for(int j=1;j<=m;j++)          {            map[i][j]=getchar();            if(map[i][j]=='M')              mx=i,my=j;            else if(map[i][j]=='G')              gx=i,gy=j;            else if(map[i][j]=='Z')            {                if(zx1==-1)                  zx1=i,zy1=j;                else                  zx2=i,zy2=j;            }          }        memset(vis,0,sizeof(vis));        while(!q[0].empty())          q[0].pop();        while(!q[1].empty())          q[1].pop();        q[0].push(mx<<10|my);        q[1].push(gx<<10|gy);        vis[mx][my][0]=true;        vis[gx][gy][1]=true;        s=1;        while(q[0].size()||q[1].size())        {            if(bfs(0,s)) {flag=true;break;}            if(bfs(0,s)) {flag=true;break;}            if(bfs(0,s)) {flag=true;break;}            if(bfs(1,s)) {flag=true;break;}            s++;        }        if(flag)          printf("%d\n",s);        else          puts("-1");    }    return 0;}
原创粉丝点击