[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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章