[kuangbin带你飞]专题一 简单搜索(1-5)

总结&&up(因人而异,这只是我个人的)

  1. 对于DFSDFS的下次递归别乱放,否则容易TLETLE
  2. 由于BFSBFS要入队,所以要记忆化(否则可能会超内存),防止一个位置重复入队

1.棋盘问题(类似8皇后问题:DFSDFS)

在这里插入图片描述
在这里插入图片描述

题意:

有一个棋盘,只能在‘#’里放棋子,保证行列不冲突,问有几种方法。

思路:

  1. 用一个棋子数组,标记所放棋子的列号,保证列不冲突。
  2. 暴力枚举第ii行 的每一列是否能放棋子,(当前行,可以放也可以不放)
  3. 放了k个就停止。

反思

对于DFSDFS的下次递归别乱放,否则容易TLETLE

AC

#include <iostream>
#include <cstring>
#define mst(x,a) memset(x,a,sizeof(x))
#define For(i,x,y) for(int i=(x); i<=(y); i++)
using namespace std;
int g[10][10],a[10];
int n,k;
string s;
int dfs(int cur, int tot)
{
    int ans=0;
    if(tot==k)return 1;
    if(cur>n)return 0;//
    For(i,1,n)
    {
        if(!a[i]&&g[cur][i]==1)
        {
            a[i]=1;
            ans+=dfs(cur+1,tot+1);
            a[i]=0;
            //ans+=dfs(cur+1,tot);///一开始放错位置了,导致tle
        }
    }
    ans+=dfs(cur+1,tot);//应该放在这里,因为当前行不放,那么就不用进for循环里
    return ans;
}
int main()
{
    while(cin>>n>>k&&(n!=-1&&k!=-1))
    {
        mst(g,0);
        For(i,1,n)
        {
            cin>>s;
            a[i]=0;
            for(int j=0; j<s.size(); j++)if(s[j]=='#')g[i][j+1]=1;
        }
        cout<<dfs(1,0)<<endl;
    }
    return 0;
}

2.POJ 2251 Dungeon Master(三维走迷宫,BFSBFS

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

题意:

有一个三维迷宫,问是否能走出。如果能走出输出最短路,否则输出“IMPOSSIBLE”.

思路:

  1. 由于三维,所以方向多加一个上下即可,变成六个方向的BFSBFS
  2. 由于要最短路,边权为1,所以直接跑BFSBFS

AC

#include <iostream>
#include <cstring>
#include <string>
#include <cstdio>
#include <queue>
#define mp make_pair
#define mst(x,a) memset(x,a,sizeof(x))
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
int flag=0;
int g[40][40][40];
int l,r,c;
int dx[6]={0,0,0,0,-1,1};
int dy[6]={0,0,1,-1,0,0};
int dz[6]={1,-1,0,0,0,0};
int ls,xs,ys,le,xe,ye;
int ans=0;
struct point{int x,y,z,dis;};
void bfs(int z, int x, int y)
{
    point fi={x,y,z,0};//定义结构体时,注意变量的赋值顺序,吐了qwq。
    g[z][x][y]=1;
    queue<point>q;
    q.push(fi);
    while(!q.empty())
    {
        point top=q.front();q.pop();
        z=top.z,x=top.x;y=top.y;
        //cout<<z<<' '<<x<<' '<<y<<endl;
        //cout<<g[z][x][y]<<endl;
        int dis=top.dis;
        if(z==le&&xe==x&&y==ye)
        {
            ans=dis;
            flag=1;return;
        }
        For(i,0,5)
        {
            int tx,ty,tz;
            tx=x+dx[i];
            ty=y+dy[i];
            tz=z+dz[i];
            if(tz>=1&&tz<=l&&ty<=c&&ty>=1&&tx>=1&&tx<=r&&!g[tz][tx][ty])
            {
                //cout<<tz<<' '<<tx<<' '<<ty<<endl;
               // cout<<g[tz][tx][ty]<<endl;
                g[tz][tx][ty]=1;
                q.push({tx,ty,tz,dis+1});
            }
        }
    }
}
int main()
{
    while(cin>>l>>r>>c&&(l|r|c))
    {
        mst(g,0);flag=0;
        string s[40];
        For(i,1,l)
        {
            For(j,1,r)cin>>s[j];
            //getchar();
            For(j,1,r)
            {
                For(k,0,c-1)
                {
                    if(s[j][k]=='S')xs=j,ys=k+1,ls=i;
                    if(s[j][k]=='E')xe=j,ye=k+1,le=i;
                    if(s[j][k]=='#')g[i][j][k+1]=1;
                }
            }
        }
        bfs(ls,xs,ys);
        if(flag)cout<<"Escaped in "<<ans<<" minute(s)."<<endl;
        else cout<<"Trapped!"<<endl;
    }
    return 0;
}

3.POJ 3278 Catch That Cow(经典BFSBFS

在这里插入图片描述

题意:

给你农夫的位置和牛的位置,问你农夫最少用多少时间可以抓到牛。
农夫每一步有三种op:

  1. x+1x+1
  2. x1x-1
  3. x2x*2

思路:

由于是每步操作,有层次,所以跑BFSBFS

反思

由于BFSBFS要入队,所以要记忆化(否则可能会超内存),防止一个位置重复入队

AC

#include <iostream>
#include <queue>
#define fi first
#define se second
#define mp make_pair
using namespace std;
typedef pair<int,int>pa;
const int maxn=1e5+10;
const int inf=0x3f3f3f3f;
queue<pa>q;
int vis[maxn];//注意记忆化,已经入队的元素,就不要重复入队了。否则会爆内存。
int main()
{
    int n,k,ans=0;
    cin>>n>>k;
    vis[n]=1;
    q.push(mp(n,0));
    while(!q.empty())
    {
        int x=q.front().fi,time=q.front().se; q.pop();
        if(x==k){ans=time;break;}
        if(x+1<=maxn-1&&!vis[x+1])vis[x+1]=1,q.push(mp(x+1,time+1));
        if(x-1>=0&&!vis[x-1])vis[x-1]=1,q.push(mp(x-1,time+1));
        if(x*2<=maxn-1&&!vis[x*2])vis[x*2]=1,q.push(mp(x*2,time+1));
    }
    cout<<ans<<endl;
    return 0;
}

4.POJ 3279 Fliptile(经典棋盘翻转问题|开灯问题)

在这里插入图片描述
在这里插入图片描述

题意:

给你一个矩阵,每个小格有一块瓦,瓦有黑白两色。
每一次翻转,都会使相邻的也翻转。
求字典序最小的翻转。

思路:

  1. 可以枚举第一行的switch(总共最多col2col^2)而col最大也15,所以放心枚举。
  2. 之后就和扫雷差不多了,有了前面的基础,去推后面的。
  3. 根据switch去改变第一行的颜色。第一行剩下的没变白的,由下一行改变,
  4. 操作结束后,最后看最后一行是否变白。

反思

  1. 把一个数的某一位(二进制)变为1: x=1<<ix|=1<<i
  2. 把一个数的某一位(二进制)变为0: xx&=~(1<<ii
  3. 把一个数的某一位(二进制)取反: xx^=1<<ii
  4. 找到一个数的某一位(二进制): (xx>>ii)&1
  5. 数组的复制。memcpy(light,orlight,sizeof(orlight))

AC

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
int orlight[30];
int a,n,m;
//char s[10];
int light[30];
int result[30];//int is 4bits ,but char is 1 bit.
//if the data is 1<<8,we can just use char to store the data.
//other else ,we must use int or long long in instead.
int get_bit(int c,int i){return (c>>i)&1;}
void change(int bit, int t)
{
    if(a)orlight[t]|=(1<<bit);
    else orlight[t] &= ~(1<<bit);
}
void init()
{
    memset(orlight,0,sizeof(orlight));
    for(int i=0; i<n; i++)
    {
        for(int j=0; j<m; j++)
        {
            cin>>a;
            change(j,i);
        }
    // cout<<(int)orlight[i]<<endl;
    }
}
void turnlight(int &x, int pos)
{
    x^=1<<pos;
}
/*
void copy_light()
{
    for(int i=0; i<n; i++)light[i]=orlight[i];
}
*/
void revers(int t)
{
    for(int i=0; i<m; i++)
    {
        //if(i!=m-1)
        cout<<get_bit(result[t],i)<<' ';
          //  cout<<((result[t]>>i)&1)<<" ";ok
          //cout<<((result[t]&(1<<i))>>i)<<" ";no
       // else
            //cout<<((result[t]>>i)&1)<<endl;
    }
    cout<<endl;
}
void solve()
{
    int switch_num=0;
    bool flag=0;
    for(int i=0; i<(1<<m); i++)
    {
        if(flag)break;
        //copy_light();
        memcpy(light,orlight,sizeof(orlight));//!!!!!!!
        switch_num=i;
        for(int j=0; j<n; j++)
        {
            result[j]=switch_num;
            for(int k=0; k<m; k++)
            {
                if((switch_num&(1<<k)))
                {
                    turnlight(light[j],k);//light[j]^=1<<k;
                    if(k>0)turnlight(light[j],k-1);
                    if(k+1<m)turnlight(light[j], k+1);
                }
            }
            if(j+1<n)light[j+1]^=switch_num;
            switch_num=light[j];//if(j==n-1&&light[j]==0)flag=1;
        }
        if(light[n-1]==0)flag=1;
    }
    if(flag)for(int i=0; i<n; i++)revers(i);
    else cout<<"IMPOSSIBLE"<<endl;
}
int main()
{
    while(cin>>n>>m)
    {
        init();
        solve();
    }
    return 0;
}
/*
bool check()
{
    for(int i=0; i<5; i++)if(light[i]!=0)return false;
    return true;
}
int getbit(int num, int bit)
{
    return
}
*/

5.POJ 1426 Find The Multiple(经典BFSBFS和同余)

在这里插入图片描述
在这里插入图片描述

题意:

给你一个数n,要你找到它的一个倍数m,m只能由1,0组成。

思路:

  1. 对于m的最高位肯定是1.
  2. 之后就跑bfs。看队首元素是否满足front%n=0
  3. 不满足,就对m*10,之后把新的m和m+1入队.
  4. 由于是大数,所以用同余优化一下。
  5. 而答案可以通过记录操作数来取得。(操作就是*10,之后+0或者+1)

AC

#include <iostream>
#include <queue>
#include <vector>
using namespace std;
int main()
{
    ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
    int n,cnt;
    while(cin>>n&&n)
    {
        cnt=0;
        vector<int>ans;
        queue<int>q;q.push(1);
        while(!q.empty())
        {
            int top=q.front(); q.pop();
            cnt++;//统计总共进行了几次操作。
            if(top%n==0)break;
            q.push( (top%n*(10%n))%n );
            q.push( (top%n*(10%n))%n+1 );
        }
        /// bfs队列常见技巧,根据总操作数,逆堆。
        for(int i=cnt; i; i/=2)ans.push_back(i%2);
        //由于每次操作不是加0就是1,所以可以根据总操作数,来逆推出答案
        for(int i=ans.size()-1; i>=0; i--)cout<<ans[i];
        cout<<endl;
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章