1. 程式人生 > >雙指標,BFS和圖論(二)

雙指標,BFS和圖論(二)

(一)BFS

1.地牢大師

你現在被困在一個三維地牢中,需要找到最快脫離的出路!

地牢由若干個單位立方體組成,其中部分不含岩石障礙可以直接通過,部分包含岩石障礙無法通過。

向北,向南,向東,向西,向上或向下移動一個單元距離均需要一分鐘。

你不能沿對角線移動,迷宮邊界都是堅硬的岩石,你不能走出邊界範圍。

請問,你有可能逃脫嗎?

如果可以,需要多長時間?

輸入格式

輸入包含多組測試資料。

每組資料第一行包含三個整數 L,R,C 分別表示地牢層數,以及每一層地牢的行數和列數。

接下來是 L 個 R 行 C 列的字元矩陣,用來表示每一層地牢的具體狀況。

每個字元用來描述一個地牢單元的具體狀況。

其中, 充滿岩石障礙的單元格用”#”表示,不含障礙的空單元格用”.”表示,你的起始位置用”S”表示,終點用”E”表示。

每一個字元矩陣後面都會包含一個空行。

當輸入一行為”0 0 0”時,表示輸入終止。

輸出格式

每組資料輸出一個結果,每個結果佔一行。

如果能夠逃脫地牢,則輸出”Escaped in x minute(s).”,其中X為逃脫所需最短時間。

如果不能逃脫地牢,則輸出”Trapped!”。

資料範圍

1≤L,R,C≤100

輸入樣例:

3 4 5
S....
.###.
.##..
###.#

#####
#####
##.##
##...

#####
#####
#.###
####E

1 3 3
S##
#E#
###

0 0 0

輸出樣例:

Escaped in 11 minute(s).
Trapped!

解題思路:一道三維的BFS搜尋題,我們可以建立三個移動陣列:vx,vy,vk,分別表示北,南,東,西,上,下,設定一個三維的map陣列來儲存地圖,
設定一個vis陣列,用來判斷是否走過以及距離。
程式碼:
#include<iostream>
#include<queue>
#include<cstring>
using namespace std;
const int N=110;
int l,r,c;
char map[N][N][N];
int vis[N][N][N];
int vx[]={1,-1,0,0,0,0};
int vy[]={0,0,1,-1,0,0};
int vk[]={0,0,0,0,1,-1};
typedef struct Node
{
    int k,x,y;
};
bool check(int K,int X,int Y)
{
    if(X<0||X>=r||Y<0||Y>=c||K<0||K>=l)
        return false;
    if(map[K][X][Y]=='#')
        return false;
    if(vis[K][X][Y]!=0)
        return false;
    return true;
}
int bfs(Node start)
{
    queue<Node> q;
    memset(vis,0,sizeof(vis));
    q.push(start);
    while(!q.empty())
    {
        Node tem=q.front();
        if(map[tem.k][tem.x][tem.y]=='E')
            return vis[tem.k][tem.x][tem.y];
        q.pop();
        for(int i=0;i<6;i++)
        {
            int X=tem.x+vx[i];
            int Y=tem.y+vy[i];
            int K=tem.k+vk[i];
            if(check(K,X,Y)==false)
                continue;
            vis[K][X][Y]=vis[tem.k][tem.x][tem.y]+1;
            Node t={K,X,Y};
            q.push(t);
        }
    }
    return 0;
}

int main()
{
    int i,j,bx,by,bk,k;
    Node start;
    string ss;
    while(1)
    {
        cin>>l>>r>>c;
        if(l==0&&r==0&&c==0)
            break;
        for(k=0;k<l;k++)
        {
            for(i=0;i<r;i++)
            {
                for(j=0;j<c;j++)
                {
                    cin>>map[k][i][j];
                    if(map[k][i][j]=='S')
                    {
                        bk=k,bx=i,by=j;
                        start={bk,bx,by};
                    }
                }
            }
            getline(cin,ss);
        }
    int ans=bfs(start);
    if(ans)
        cout<<"Escaped in "<<ans<<" minute(s)."<<endl;
    else
        cout<<"Trapped!"<<endl;
    }
    return 0;
}

2.全球變暖

你有一張某海域 N×N 畫素的照片,”.”表示海洋、”#”表示陸地,如下所示:

.......
.##....
.##....
....##.
..####.
...###.
.......

其中”上下左右”四個方向上連在一起的一片陸地組成一座島嶼,例如上圖就有 2 座島嶼。

由於全球變暖導致了海面上升,科學家預測未來幾十年,島嶼邊緣一個畫素的範圍會被海水淹沒。

具體來說如果一塊陸地畫素與海洋相鄰(上下左右四個相鄰畫素中有海洋),它就會被淹沒。

例如上圖中的海域未來會變成如下樣子:

.......
.......
.......
.......
....#..
.......
.......

請你計算:依照科學家的預測,照片中有多少島嶼會被完全淹沒。

輸入格式

第一行包含一個整數N。

以下 N 行 N 列,包含一個由字元”#”和”.”構成的 N×N 字元矩陣,代表一張海域照片,”#”表示陸地,”.”表示海洋。

照片保證第 1 行、第 1 列、第 N 行、第 N 列的畫素都是海洋。

輸出格式

一個整數表示答案。

資料範圍

1≤N≤1000

輸入樣例1:

7
.......
.##....
.##....
....##.
..####.
...###.
.......

輸出樣例1:

1

輸入樣例2:

9
.........
.##.##...
.#####...
.##.##...
.........
.##.#....
.#.###...
.#..#....
.........

輸出樣例2:

1
 解題思路:該題要找出完全被淹沒的島嶼的個數,首先我們要做的是找出所有的聯通快,我們可以用bfs來找,對於每一個聯通快我們需要判斷他是否被完全淹沒,如何判斷呢? 我們可以找出該聯通塊裡一共有多少個畫素,再找出有多少個畫素與海相鄰,如果兩者的個數相等,那麼該島嶼必然會被完全淹沒。 否則,不會完全淹沒。特別注意:當你用bfs開始尋找時,此時的畫素總數的初始值為1. 程式碼:
#include<iostream>
#include<cstring>
#include<queue>
#include<cstdio>
using namespace std;
const int N=1010;
char map[N][N];
bool ts[N][N];
int vx[]={1,-1,0,0};
int vy[]={0,0,1,-1};
int ans,n;
typedef struct Node
{
    int x,y;
};
bool check(int X,int Y)
{
    if(X<0||X>=n||Y<0||Y>=n)
        return false;
    if(map[X][Y]=='.')
        return false;
    if(ts[X][Y]==true)
        return false;
    return true;
}
void bfs(int i,int j)
{
    Node start={i,j};
    queue<Node> q;
    ts[i][j]=true;
    q.push(start);
    int total=1,ver=0;
    while(q.size())
    {
        Node t=q.front();
        q.pop();
        int flag=false;
        for(i=0;i<4;i++)
        {
            int X=t.x+vx[i];
            int Y=t.y+vy[i];
            if(X>=0&&X<n&&Y>=0&&Y<n&&map[X][Y]=='.')
            {
                flag=true;
            }
            if(check(X,Y)==false)
                continue;
            total++;
            ts[X][Y]=true;
            Node f={X,Y};
            q.push(f);
        }
        if(flag)
            ver++;
    }
    if(total==ver)
        ans++;
}
int main()
{
    int i,j;
    cin>>n;
    for(i=0;i<n;i++)
    {
        for(j=0;j<n;j++)
        {
            cin>>map[i][j];
        }
    }
    for(i=0;i<n;i++)
    {
        for(j=0;j<n;j++)
        {
            if(!ts[i][j]&&map[i][j]=='#')
            {
                bfs(i,j);
            }
        }
    }
    cout<<ans;
    return 0;
}

3.完全二叉樹的權值

給定一棵包含 N 個節點的完全二叉樹,樹上每個節點都有一個權值,按從上到下、從左到右的順序依次是 A1,A2,⋅⋅⋅AN,如下圖所示:

現在小明要把相同深度的節點的權值加在一起,他想知道哪個深度的節點權值之和最大?

如果有多個深度的權值和同為最大,請你輸出其中最小的深度。

注:根的深度是 1。

輸入格式

第一行包含一個整數 N。

第二行包含 N 個整數 A1,A2,⋅⋅⋅AN。

輸出格式

輸出一個整數代表答案。

資料範圍

1≤N≤105,
−105≤Ai≤105

輸入樣例:

7
1 6 5 4 3 2 1

輸出樣例:

2
解題思路:每一層的個數都是=2n-1個,而且開頭的下標都是2的倍數
程式碼:
#include<iostream>
using namespace std;
const int N=100010;
typedef long long ll;
ll a[N];
ll maxn,sum,ans;
int main()
{
    ll i,j,n,k;
    cin>>n;
    for(i=1;i<=n;i++)
        cin>>a[i];
    maxn=a[1];
    ans=1;
    k=1;
    for(i=2;i<=n;i=i*2)
    {
        sum=0;
        for(j=i;j<=i*2-1&&j<=n;j++)
        {
            sum+=a[j];
        }
        k++;
        if(sum>maxn)
        {
            maxn=sum;
            ans=k;
        }
    }
    cout<<ans;
    return 0;
}