1. 程式人生 > >2017.5 校內預選賽 A H

2017.5 校內預選賽 A H

判斷 留言 個人 什麽 一段 函數 dir urn 技術

題目描述:

目前圖像識別是一項非常熱門的技術,最流行的莫不過是深度學習的圖像識別,識別率甚至能達到99%以上。當然,對於簡單的圖像來說,深度學習是沒有必要的。比如如果要識別安徽拼音的首字母A和H,就可以不用深度學習就可以判斷。現在有一些只含A或者H的圖像,你知道該如何識別嗎?

輸入描述:

第一行輸入正整數T,表示數據的組數。

每組數據中,第一行是兩個正整數n和m,表示圖像的大小。

接下來有n行,每行m個字符,只可能為‘.‘或者‘#‘。‘.‘表示白色,‘#‘表示黑色。‘#‘會通過上下左右或者左上左下右上右下連成一個區域,該區域表示字母。

數據保證字母在圖像內,不會有缺失。

數據保證圖像內只含有A或者H,且除字母外無其他黑色區域。不存在空白圖像或者含有其他內容的數據。

註意,字母不一定是正著的,有可能是斜著橫著或者倒著的。

特別提示:圖像一定是白底黑字的,不會存在反色的情況。

輸出描述

對於每行數據,輸出‘Case t: x‘,x表示你所識別出來的字母。

輸入樣例:

2

3 6

######

..#.#.

...#..

9 21

.....................

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

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

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

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

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

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

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

.....................

輸出樣例:

Case 1: A

Case 2: H

…..#.#

….###

…#.#.

其實這個題目是我們參加安徽省程序設計大賽時候的一個題目,ljm學長又把這個題目放到校內預選賽中,算是其中的一個較為簡單的題目(ps: 團隊賽感覺比個人賽有趣)

我看見這個題目的思路還是想找A H 這連個字母有是不同,剛開始想到的還是A和H的兩個邊是不同的,H的兩個邊平行,A則相交,但是題目A 和 H 但有些樣例是不規則的形狀,這樣仔細想下去,會變得很復雜,所以得轉換下思路。那該怎麽辦呢?

其實可以註意到,A中有個用#圍起來的封閉區域,而H沒有,那怎麽找到這個區域,圖的遍歷,我用的是dfs。代碼如下:

#include<iostream>
#include<memory.h>
using namespace std;

int dir1[4]={0,1,0,-1};
int dir2[4]={1,0,-1,0};
int f[100][100]; //表示是否遍歷過
char filed[100][100];

void dfs(int x,int y,int n,int m, int &flag){ //引用類型很重要 
    f[x][y]=1;
    for(int i=0;i<4;i++){ //從四個方向遞歸 
        int dx=x+dir1[i];
        int dy=y+dir2[i];
        if(dx<0 || dx>=n || dy<0 || dy>=m) //判斷是否越界 
            flag=1;
        else
            if(filed[dx][dy]==. && f[dx][dy]==0)
                dfs(dx,dy,n,m,flag);
    }
} 

bool solve(int n,int m){
    for(int i=0;i<n;i++){
        for(int j=0;j<m;j++){
            if(filed[i][j]==. && f[i][j]==0){            
            //選 filed[i][j]==. 並且沒有訪問過的點進行搜索 我這裏用的是dfs 其實也可以用bfs 
                int flag=0;
                dfs(i,j,n,m,flag);
                if(flag==0)
                    return true;
            }
        }
    }
    return  false;
}
int main(){
    int t;
    cin>>t;
    for(int k=1;k<=t;k++){
        memset(f,0,100*100); //每個樣例都重置一下f 
        int n,m;    //圖的大小 
        cin>>n>>m;
        
        for(int i=0;i<n;i++){
            for(int j=0;j<m;j++){
                cin>>filed[i][j];
            } 
                
        }
        if(solve(n,m))
            cout<<"Case "<<k<<": "<<"A"<<endl;
        else
            cout<<"Case "<<k<<": "<<"H"<<endl;
        
    }    
} 

解釋下:solve函數裏面兩個循環,對圖中的所有點進行以此if判斷,然後對值為‘.‘且沒有訪問過的點開始遍歷,下圖為栗子

技術分享

從換紅線的那個點開始遍歷。那就進入dfs函數。看下面一段代碼:

for(int i=0;i<4;i++){ //從四個方向遞歸 
        int dx=x+dir1[i];
        int dy=y+dir2[i];
        if(dx<0 || dx>=n || dy<0 || dy>=m) //判斷是否越界 
            flag=1;
        else
            if(filed[dx][dy]==. && f[dx][dy]==0)
                dfs(dx,dy,n,m,flag);
    }

i從0到3代表四個方向,得到下一步應該到的坐標dx dy 然後對dx dy 進行判斷,如歌超出0-n 0-m範圍,說明x,y 處於邊界,如上圖所代表的點,那就說明它所在區域不可能四周都被#圍住,這個點就是個出口。那麽就把它的flag置為1。

A中有一個區域被#圍起來,所以flag不會被置為1,還是0,所以在solve中對flag進行判斷,如果flag==0 為true 說明為A 直接返回true到主函數。問題得解

ps: 註意dfs中的參數是對flag的引用,大家可以把引用去掉看看有什麽結果。(有助於理解遞歸哦)

看了這篇題解不能理解的同學,可以花一個圖按照程序手動推演下這個程序。(可以後臺留言,盡量回復)

2017.5 校內預選賽 A H