《挑战程序设计竞赛》2.1.1 深度优先搜索 POJ2386 POJ1979 AOJ0118 AOJ0033 POJ3009

POJ2386

Lake Counting
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 25366   Accepted: 12778

Description

Due to recent rains, water has pooled in various places in Farmer John's field, which is represented by a rectangle of N x M (1 <= N <= 100; 1 <= M <= 100) squares. Each square contains either water ('W') or dry land ('.'). Farmer John would like to figure out how many ponds have formed in his field. A pond is a connected set of squares with water in them, where a square is considered adjacent to all eight of its neighbors. 

Given a diagram of Farmer John's field, determine how many ponds he has.

Input

* Line 1: Two space-separated integers: N and M 

* Lines 2..N+1: M characters per line representing one row of Farmer John's field. Each character is either 'W' or '.'. The characters do not have spaces between them.

Output

* Line 1: The number of ponds in Farmer John's field.

Sample Input

10 12
W........WW.
.WWW.....WWW
....WW...WW.
.........WW.
.........W..
..W......W..
.W.W.....WW.
W.W.W.....W.
.W.W......W.
..W.......W.

Sample Output

3

Hint

OUTPUT DETAILS: 

There are three ponds: one in the upper left, one in the lower left,and one along the right side.

Source

USACO 2004 November

思路:

园子里下了雨有积水,八连通的积水被认为是连接在一起的。求园子里有多少水洼。

确实是比较经典的深搜。


代码:

Source Code

Problem: 2386		User: liangrx06
Memory: 1320K		Time: 16MS
Language: G++		Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;

#define N 100

int n, m;
char lake[N][N+1];

void input()
{
    cin >> n >> m;
    for (int i=0; i<n; i++) {
        cin >> lake[i];
    }
}

void dfs(int x, int y)
{
    lake[x][y] = '.';
    for (int dx=-1; dx<=1; dx++) {
        for (int dy=-1; dy<=1; dy++) {
            if (!dx && !dy)
                continue;
            int nx = x + dx, ny = y + dy;
            if (0 <= nx && nx < n && 0 <= ny && ny < m
                && lake[nx][ny] == 'W')
                dfs(nx, ny);
        }
    }
}

void solve() {
    int res = 0;
    for (int i=0; i<n; i++) {
        for (int j=0; j<m; j++) {
            if (lake[i][j] == 'W') {
                dfs(i, j);
                res ++;
            }
        }
    }
    printf("%d\n", res);
}

int main(void)
{
    input();

    solve();

    return 0;
}


POJ1979

Red and Black
Time Limit: 1000MS   Memory Limit: 30000K
Total Submissions: 27075   Accepted: 14714

Description

There is a rectangular room, covered with square tiles. Each tile is colored either red or black. A man is standing on a black tile. From a tile, he can move to one of four adjacent tiles. But he can't move on red tiles, he can move only on black tiles. 

Write a program to count the number of black tiles which he can reach by repeating the moves described above. 

Input

The input consists of multiple data sets. A data set starts with a line containing two positive integers W and H; W and H are the numbers of tiles in the x- and y- directions, respectively. W and H are not more than 20. 

There are H more lines in the data set, each of which includes W characters. Each character represents the color of a tile as follows. 

'.' - a black tile 
'#' - a red tile 
'@' - a man on a black tile(appears exactly once in a data set) 
The end of the input is indicated by a line consisting of two zeros. 

Output

For each data set, your program should output a line which contains the number of tiles he can reach from the initial tile (including itself).

Sample Input

6 9
....#.
.....#
......
......
......
......
......
#@...#
.#..#.
11 9
.#.........
.#.#######.
.#.#.....#.
.#.#.###.#.
.#.#..@#.#.
.#.#####.#.
.#.......#.
.#########.
...........
11 6
..#..#..#..
..#..#..#..
..#..#..###
..#..#..#@.
..#..#..#..
..#..#..#..
7 7
..#.#..
..#.#..
###.###
...@...
###.###
..#.#..
..#.#..
0 0

Sample Output

45
59
6
13

Source

Japan 2004 Domestic

思路:

人的初始位置是@,点代表黑色地板可以走,#代表白色不能走,每次只可以上下左右走一步。问能到达多少黑色地板。

深搜即可。


代码:

Source Code

Problem: 1979		User: liangrx06
Memory: 212K		Time: 0MS
Language: C++		Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
using namespace std;

#define N 20

int n, m;
char tile[N][N+1];
int number;

void dfs(int x, int y)
{
    tile[x][y] = '#';
    int d[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
    for (int i = 0; i < 4; i ++) {
        int nx = x + d[i][0], ny = y + d[i][1];
        if (0 <= nx && nx < n && 0 <= ny && ny < m
                && tile[nx][ny] == '.') {
            number ++;
            dfs(nx, ny);
        }
    }
}

void solve() {
    char *c;
    int bx, by;
    for (int i=0; i<n; i++) {
        if ( c = strstr(tile[i], "@") ) {
            bx = i;
            by = c - tile[i];
        }
    }
    number = 1;
    dfs(bx, by);
    printf("%d\n", number);
}

int main(void)
{
    while (cin >> m >> n) {
        if (!m && !n)
            break;

        for (int i=0; i<n; i++) {
            cin >> tile[i];
        }

        solve();
    }

    return 0;
}


AOJ0118

Time Limit: 1000MS   Memory Limit: 65536KB   64bit IO Format: %lld & %llu

Submit Status

Description

タナカ氏が HW アールの果樹園を残して亡くなりました。果樹園は東西南北方向に H×W の区画に分けられ、区画ごとにリンゴ、カキ、ミカンが植えられています。タナカ氏はこんな遺言を残していました。

果樹園は区画単位でできるだけ多くの血縁者に分けること。ただし、ある区画の東西南北どれかの方向にとなりあう区画に同じ種類の果物が植えられていた場合は、区画の境界が分からないのでそれらは 1 つの大きな区画として扱うこと。

例えば次のような 3x10 の区画であれば(リはリンゴ、カはカキ、ミはミカンを表す)

同じ樹がある区画の間の境界を消すと次のようになり、

結局 10 個の区画、つまり 10 人で分けられることになります。 雪が降って区画の境界が見えなくなる前に分配を終えなくてはなりません。あなたの仕事は果樹園の地図を もとに分配する区画の数を決めることです。ということで、果樹園の地図を読み込み、分配を受けら れる血縁者の人数を出力して終了するプログラムを作成してください。ただし、果樹園の地図は W 文字×H 行の文字列として与えられます。この文字列には、リンゴを表す@、カキを表す#、ミカンを表す*、の 3 文字しか使われていません。

Input

複数のデータセットが与えられます。各データセットは空白で区切られたH Wを含む行から始まり、続いてH × Wの文字が与えられます。入力はゼロが2つの行で終わります。

H, W は100以下です。

Output

各データセットごとに、分配を受ける人数を1行に出力してください。

Sample Input

10 10
####*****@
@#@@@@#*#*
@##***@@@*
#****#*@**
##@*#@@*##
*@@@@*@@@#
***#@*@##*
*@@@*@@##@
*@*#*@##**
@****#@@#@
0 0

Output for the Sample Input

33

Source

PC Koshien 2005 , All-Japan High School Programming Contest, Aizu-Wakamatsu, Japan, 2005 
(extended version) 
http://www.pref.fukushima.jp/pc-concours/


思路:

原题是日文版的,中文题意是:
在H * W的矩形果园里有苹果、梨、蜜柑三种果树, 相邻(上下左右)的同种果树属于同一个区域,给出果园的果树分布,求总共有多少个区域。 (原题的样图中苹果为リ,梨为カ,蜜柑为ミ, 图中共10个区域) 
输入:
多组数据,每组数据第一行为两个整数H,W(H <= 100, W <= 100), H =0 且 W = 0代表输入结束。以下H行W列表示果园的果树分布, 苹果是@,梨是#, 蜜柑是*。 
输出:
对于每组数据,输出其区域的个数。

解题思路是对未搜索过的每个点进行神搜,搜索过的位置置为‘X’,在最上层函数中进行计数。


代码:

#include <iostream>
#include <cstdio>
using namespace std;

#define N 100

int n, m;
char tile[N][N+1];
int number;

void dfs(int x, int y, char c)
{
    tile[x][y] = 'X';
    int d[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
    for (int i = 0; i < 4; i ++) {
        int nx = x + d[i][0], ny = y + d[i][1];
        if (0 <= nx && nx < n && 0 <= ny && ny < m
                && tile[nx][ny] == c) {
            dfs(nx, ny, c);
        }
    }
}

void solve() {
    int res = 0;
    for (int i=0; i<n; i++) {
        for (int j=0; j<m; j++) {
            if (tile[i][j] != 'X') {
                dfs(i, j, tile[i][j]);
                res ++;
            }
        }
    }
    printf("%d\n", res);
}

int main(void)
{
    while (cin >> n >> m) {
        if (!m && !n)
            break;

        for (int i=0; i<n; i++) {
            cin >> tile[i];
        }

        solve();
    }

    return 0;
}


AOJ0033

Time Limit: 1000MS   Memory Limit: 65536KB   64bit IO Format: %lld & %llu

Submit Status

Description

図のように二股に分かれている容器があります。1 から10 までの番号が付けられた10 個の玉を容器の開口部 A から落とし、左の筒 B か右の筒 C に玉を入れます。板 D は支点 E を中心に左右に回転できるので、板 D を動かすことで筒 B と筒 C のどちらに入れるか決めることができます。開口部 A から落とす玉の並びを与えます。それらを順番に筒 B 又は筒 Cに入れていきます。このとき、筒 B と筒 C のおのおのが両方とも番号の小さい玉の上に大きい玉を並べられる場合は YES、並べられない場合は NO と半角大文字で出力して終了するプログラムを作成してください。ただし、容器の中で玉の順序を入れ替えることはできないものとします。また、続けて同じ筒に入れることができるものとし、筒 B, C ともに10 個の玉がすべて入るだけの余裕があるとします。

Input

複数のデータセットが与えられます。1行目にデータセット数 N が与えられます。つづいて、N 行のデータセットが与えられます。各データセットに 10 個の番号が与えられます。左から1番目の玉の番号、2番目の玉の番号、、、といった順番です。

Output

各データセットに対して、YES または NO を出力して下さい。

Sample Input

2
3 1 4 2 5 6 7 8 9 10
10 9 8 7 6 5 4 3 2 1

Output for the Sample Input

YES
NO

Source

PC Koshien 2004 , All-Japan High School Programming Contest, Aizu-Wakamatsu, Japan, 2004 
http://www.pref.fukushima.jp/pc-concours/


思路:

有一个形似央视大楼的筒,从A口可以放球,放进去的球可通过挡板DE使其掉进B管或C管里,现有带1-10标号的球按给定顺序从A口放入,问是否有一种控制挡板的策略可以使B管和C管中的球从下往上标号递增。  

输入:
第一行输入数据组数N。接下来N行为N组具体数据,每组数据中有10个整数,代表球的放入顺序。
输出:
对于每组数据,若策略存在,输出YES;若不存在,输出NO。


用穷举或者DFS搜索都可以。我这里用了代码相对简单的枚举。位运算能够降低存储。


代码:

#include <iostream>
#include <cstdio>
using namespace std;

const int N = 10;

int a[N];

void input()
{
    for (int i = 0; i < N; i ++) {
        scanf("%d", &a[i]);
    }
}

void solve()
{
    bool res = false;
    for (int k = 0; k < (1<<10); k++) {
        int num = 0;
        int i;
        for (i = 0; i < N; i ++) {
            if ( (1 << i)&k ) {
                if (a[i] > num)
                    num = a[i];
                else
                    break;
            }
        }
        if (i < N)
            continue;
        num = 0;
        for (i = 0; i < N; i ++) {
            if ( ((1 << i)&k) == 0 ) {
                if (a[i] > num)
                    num = a[i];
                else
                    break;
            }
        }
        if (i == N) {
            res = true;
            //printf("k=%d\n", k);
            break;
        }
    }
    if (res)
        printf("YES\n");
    else
        printf("NO\n");
}

int main(void)
{
    int t;
    cin >> t;
    while (t--) {
        input();

        solve();
    }

    return 0;
}

POJ3009

Curling 2.0
Time Limit: 1000MS   Memory Limit: 65536K
Total Submissions: 15450   Accepted: 6390

Description

On Planet MM-21, after their Olympic games this year, curling is getting popular. But the rules are somewhat different from ours. The game is played on an ice game board on which a square mesh is marked. They use only a single stone. The purpose of the game is to lead the stone from the start to the goal with the minimum number of moves.

Fig. 1 shows an example of a game board. Some squares may be occupied with blocks. There are two special squares namely the start and the goal, which are not occupied with blocks. (These two squares are distinct.) Once the stone begins to move, it will proceed until it hits a block. In order to bring the stone to the goal, you may have to stop the stone by hitting it against a block, and throw again.

《挑战程序设计竞赛》2.1.1 深度优先搜索 POJ2386 POJ1979 AOJ0118 AOJ0033 POJ3009_第1张图片
Fig. 1: Example of board (S: start, G: goal)

The movement of the stone obeys the following rules:

  • At the beginning, the stone stands still at the start square.
  • The movements of the stone are restricted to x and y directions. Diagonal moves are prohibited.
  • When the stone stands still, you can make it moving by throwing it. You may throw it to any direction unless it is blocked immediately(Fig. 2(a)).
  • Once thrown, the stone keeps moving to the same direction until one of the following occurs:
    • The stone hits a block (Fig. 2(b), (c)).
      • The stone stops at the square next to the block it hit.
      • The block disappears.
    • The stone gets out of the board.
      • The game ends in failure.
    • The stone reaches the goal square.
      • The stone stops there and the game ends in success.
  • You cannot throw the stone more than 10 times in a game. If the stone does not reach the goal in 10 moves, the game ends in failure.

《挑战程序设计竞赛》2.1.1 深度优先搜索 POJ2386 POJ1979 AOJ0118 AOJ0033 POJ3009_第2张图片
Fig. 2: Stone movements

Under the rules, we would like to know whether the stone at the start can reach the goal and, if yes, the minimum number of moves required.

With the initial configuration shown in Fig. 1, 4 moves are required to bring the stone from the start to the goal. The route is shown in Fig. 3(a). Notice when the stone reaches the goal, the board configuration has changed as in Fig. 3(b).

《挑战程序设计竞赛》2.1.1 深度优先搜索 POJ2386 POJ1979 AOJ0118 AOJ0033 POJ3009_第3张图片
Fig. 3: The solution for Fig. D-1 and the final board configuration

Input

The input is a sequence of datasets. The end of the input is indicated by a line containing two zeros separated by a space. The number of datasets never exceeds 100.

Each dataset is formatted as follows.

the width(=w) and the height(=h) of the board 
First row of the board
 
... 
h-th row of the board

The width and the height of the board satisfy: 2 <= w <= 20, 1 <= h <= 20.

Each line consists of w decimal numbers delimited by a space. The number describes the status of the corresponding square.

0 vacant square
1 block
2 start position
3 goal position

The dataset for Fig. D-1 is as follows:

6 6 
1 0 0 2 1 0 
1 1 0 0 0 0 
0 0 0 0 0 3 
0 0 0 0 0 0 
1 0 0 0 0 1 
0 1 1 1 1 1

Output

For each dataset, print a line having a decimal integer indicating the minimum number of moves along a route from the start to the goal. If there are no such routes, print -1 instead. Each line should not have any character other than this number.

Sample Input

2 1
3 2
6 6
1 0 0 2 1 0
1 1 0 0 0 0
0 0 0 0 0 3
0 0 0 0 0 0
1 0 0 0 0 1
0 1 1 1 1 1
6 1
1 1 2 1 1 3
6 1
1 0 2 1 1 3
12 1
2 0 1 1 1 1 1 1 1 1 1 3
13 1
2 0 1 1 1 1 1 1 1 1 1 1 3
0 0

Sample Output

1
4
-1
4
10
-1

Source

Japan 2006 Domestic


思路:

题意是:已知起点和终点,求石子从起点到达终点的最短路,如果无法到达,则输出-1。

石子移动的具体规则如下:
1、开始时,石子在起点s处
2、运动方向可以是水平或垂直的,不能斜方向运动
3、最开始的时候,你可以将石子向上下左右任意一个方向抛,如果与它相邻的点是障碍物的话除外
4、一旦石子开始运动,有三种可能:
  a、遇到障碍物,石子会停在障碍物的前一格,障碍物会消失
  b、如果出界,游戏失败
  c、到达终点,游戏结束并成功
5、如果移动的次数超过10次,将认为游戏是失败的

图中各方格的标示为:

0代表无障碍;

1有障碍;

2为起点;

3为终点。

需要注意的几个地方:

1、石子碰到障碍物时障碍物会消失。也就是说每走一步图的状态都会发生改变,这导致BFS无法成功。

2、移动次数10次实际上是帮助剪枝的,最小步数也可帮助剪枝。

解法:

所以比较合适的解法是DFS,DFS前要对障碍物1更新为0,结束后更改回来。即此题的解法为DFS+回溯。


代码:

Source Code

Problem: 3009		User: liangrx06
Memory: 236K		Time: 32MS
Language: C++		Result: Accepted
Source Code
#include <iostream>
#include <cstdio>
using namespace std;

#define N 20
#define MAX_STEP 10

int n, m;
int map[N][N];
int minStep;

bool legal(int x, int y)
{
    return 0 <= x && x < n && 0 <= y && y < m;
}

void dfs(int x, int y, int step)
{
    if (step >= MAX_STEP || step >= minStep)
        return;
    int d[4][2] = {{0, 1}, {0, -1}, {1, 0}, {-1, 0}};
    for (int i = 0; i < 4; i ++) {
        int nx = x + d[i][0], ny = y + d[i][1];
        if (!legal(nx, ny) || map[nx][ny] == 1)
            continue;
        if (map[nx][ny] == 3) {
            minStep = step + 1;
            continue;
        }
        while (true) {
            nx += d[i][0], ny += d[i][1];
            if (!legal(nx, ny))
                break;
            else if (map[nx][ny] == 1) {
                map[nx][ny] = 0;
                dfs(nx-d[i][0], ny-d[i][1], step+1);
                map[nx][ny] = 1;
                break;
            }
            else if (map[nx][ny] == 3) {
                minStep = step + 1;
                break;
            }
        }
    }
}

void solve() {
    int bx, by;
    for (int i=0; i<n; i++) {
        for (int j=0; j<m; j++) {
            if (map[i][j] == 2) {
                bx = i; by = j;
            }
        }
    }
    minStep = MAX_STEP + 1;
    dfs(bx, by, 0);
    if (minStep <= MAX_STEP)
        printf("%d\n", minStep);
    else
        printf("%d\n", -1);
}

int main(void)
{
    while (cin >> m >> n) {
        if (!m && !n)
            break;

        for (int i=0; i<n; i++) {
            for (int j=0; j<m; j++) {
                scanf("%d", &map[i][j]);
            }
        }

        solve();
    }

    return 0;
}


你可能感兴趣的:(poj,DFS,AOJ,挑战程序设计竞赛)