1. 程式人生 > >[2012山東省第三屆ACM大學生程序設計競賽]——Mine Number

[2012山東省第三屆ACM大學生程序設計競賽]——Mine Number

== memory cat chan get iostream sts 設計 target

Mine Number

題目:http://acm.sdut.edu.cn/sdutoj/problem.php?

action=showproblem&problemid=2410

Time Limit: 1000ms Memory limit: 65536K 有疑問?點這裏^_^


題目描寫敘述

Every one once played the game called Mine Sweeping, here I change the rule. You are given an n*m map, every element is a ‘*‘ representing a mine, or a ‘.‘ representing any other thing. If I ask you what‘s the total number of mines around (i, j), you should check (i, j)‘s up, down, left, right and also itself (if overstep the boundary, ignore it), if that position is a ‘*‘, you should add one to the total number of (i, j), and here I call the number Mine Number. For example, if the map is "..**.. ", we can get the Mine Number as "012210" easily, but here is the question, if I give you the Mine Number, can you tell me the original map?

輸入

The input consists of multiple test cases.
The first line contains a number T, representing the number of test cases.
Then T lines follow. For each case, the first line contains two numbers n and m (1<=n, m<=20).representing the lines and rows. Then following n lines, each line contain m numbers each number represents the Mine Number.

輸出

For each case, please print the case number (beginning with 1) and the original map which you reverted. The data guarantee there is only one result.

演示樣例輸入
2
7 11
10010100101
21223221222
32354532323
32355532323
31235321333
21022201333
10001000111
5 6
001110
013431
014541
013431

001110


演示樣例輸出
Case 1:
...........
*..*.*..*.*
*.*****.*.*
*.*****.*.*
*..***..*.*
*...*...***
...........
Case 2:
......
..***.
..***.
..***.

......


來源

2012年"浪潮杯"山東省第三屆ACM大學生程序設計競賽


一道搜索題,比賽時就是研究這道題,和高恒一起。話說當時方向對了,解題思路非常正確。可是編程時候出了點問題。尤其是最後的代碼輸出,輸出一個字符和一個空格。。

。事實上應該沒有空格的。。

。o(╯□╰)o啊。。。

言歸正傳:

這道題題意非常easy,掃雷玩過吧?

技術分享 技術分享

上面的數字3,代表該點八個方向上有三顆雷。

這道題題意也差點兒相同,僅僅是數字所代表的是 上下左右與該點 五個方向的雷數量。

題目會給出數字,讓你確定 雷的分布圖。每一個數據僅僅輸出一個解。

DFS,深度優先搜索。

大體的思路方向是:

從0,0開始往後推斷。每一個點是否放雷。根據就是周圍的數字(上下左右)是否有0的情況。有0就不放雷。

放雷後就要將五個方向的數字減1,然後繼續往後推斷。

這是基本的推斷,可是顯然須要大的前提來 剪掉大半棵樹。

→首先將第一行枚舉,然後每一行依據上一行狀態來做:

假設上一行同位置數字為0,則該點不放雷。

假設上一行同位置數字為1。則該點必須放雷,此時推斷四周是否有0的情況,沒有則放雷,有則回溯。

假設上一行同位置數字不為0或者1。則回溯。

推斷到最後一行,須要將最後一行數組推斷,是否全為0,是則輸出結果。不是則回溯。


就是這樣。



#include <iostream>
#include <string.h>
using namespace std;
#define MAX 25
int n,m,num[MAX][MAX],dis[5][2]={0,0,1,0,-1,0,0,1,0,-1};
char Map[MAX][MAX];
bool ispos;
// 推斷出界
bool isout(int x,int y)
{
    if( x<0 || y<0 || x>=n || y>=m )    return 1;
    return 0;
}
// 推斷五個點是否有小於等於0的位置
bool location(int x,int y)
{
    int i,xx,yy;
    for(i=0;i<5;++i)
    {
        xx=x+dis[i][0];
        yy=y+dis[i][1];
        if( !isout(xx,yy) && num[xx][yy]<=0 )   return false;
    }
    return true;
}
// 假設放雷,五個點數字減1
void change(int x,int y)
{
    int i,xx,yy;
    for(i=0;i<5;++i)
    {
        xx=x+dis[i][0];
        yy=y+dis[i][1];
        if( isout(xx,yy) )  continue;
        --num[xx][yy];
    }
}
// 假設該地原來放雷,可是應該不放。回溯,五個點數字加1
void c_back(int x,int y)
{
    int i,xx,yy;
    for(i=0;i<5;++i)
    {
        xx=x+dis[i][0];
        yy=y+dis[i][1];
        if( isout(xx,yy) )  continue;
        ++num[xx][yy];
    }
}
// 推斷最後一行是否符合條件
bool judge_final(void)
{
    int i;
    for(i=0;i<m;++i)
        if( num[n-1][i]!=0 )    return false;
    return true;
}
// 輸出結果
void print(void)
{
    int i,j;
    for(i=0;i<n;++i)
    {
        for(j=0;j<m;++j)
            cout<<Map[i][j];
        cout<<endl;
    }
}
void dfs(int x,int y)
{
    if( ispos ) return;
    if( x==n )
    {
        if( judge_final() )
        {ispos=1;print();}
        return;
    }
    if( y==m )  {dfs(x+1,0);return;}
    if( x==0 )  // 首先第一行要進行枚舉。不沖突就可以
    {
        if( location(x,y) )
        {
            Map[x][y]=‘*‘;
            change(x,y);
            dfs(x,y+1);
            c_back(x,y);
        }
        Map[x][y]=‘.‘;
        dfs(x,y+1);
    }
    else// 其余行,依據上一行對應位置來推斷假設做
    {
        if( num[x-1][y]==0 )    // 上一行為0,此行不能放雷
        {
            Map[x][y]=‘.‘;
            dfs(x,y+1);
        }
        else if( num[x-1][y]==1 )   // 上一行為1,此行必須放雷,推斷四周是否有0情況
            if( location(x,y) )
            {
                Map[x][y]=‘*‘;
                change(x,y);
                dfs(x,y+1);
                c_back(x,y);
            }
    }
}

int main()
{
    int i,j,test,t_num;
    char c;
    cin>>test;
    for(t_num=1;t_num<=test;++t_num)
    {
        cin>>n>>m;
        for(i=0;i<n;++i)
            for(j=0;j<m;++j)
            {
                cin>>c;
                num[i][j]=c-‘0‘;
            }
        cout<<"Case "<<t_num<<":"<<endl;
        ispos=0;
        dfs(0,0);
    }
    return 0;
}


[2012山東省第三屆ACM大學生程序設計競賽]——Mine Number