1. POJ1321 棋盘问题

题目链接

题意:在给定形状的棋盘上摆无区别棋子,任意两棋子不同行或同列,求摆 k 个棋子所有方案数。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <cstdio>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
typedef long long ll;
const int INF = 2e9;

int n, k, ans, cnt;
char s[10][10], v[10];

void dfs(int row) {
    if (cnt == k) { ans++; return; }
    if (row > n) return;
    for (int i = 0; i < n; ++i)
        if (s[row][i] == '#' && !v[i]) {
            v[i] = 1;
            cnt++;
            dfs(row + 1);
            v[i] = 0;
            cnt--;
        }
    dfs(row + 1);
}

int main()
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    while (cin >> n >> k && n != -1) {
        ans = cnt = 0;
        for (int i = 1; i <= n; ++i) {
            cin >> s[i];
        }
        dfs(1);
        printf("%d\n", ans);
    }
    return 0;
}

2. POJ2251 Dungeon Master

题目链接

题意:在$L\times R\times C$的地牢给定起点终点,求最短距离。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 2e9, N = 35;

struct node {
    int x, y, z, cnt;
    node(int x = 0, int y = 0, int z = 0, int cnt = 0) : x(x), y(y), z(z), cnt(cnt) {}
};

node st, ed;
char mp[N][N][N];
int l, r, c;
int v[N][N][N];
int dx[6] = {0, 0, 0, 0, 1, -1};
int dy[6] = {1, 0, -1, 0, 0, 0};
int dz[6] = {0, 1, 0, -1, 0, 0};

bool valid(int x, int y, int z) {
    if (x >= 1 && x <= l && y >= 1 && y <= r && z >= 0 && z < c && !v[x][y][z] && mp[x][y][z] != '#') return 1;
    return 0;
}

int bfs() {
    memset(v, 0, sizeof(v));
    queue<node> q;
    q.push(st);
    v[st.x][st.y][st.z] = 1;
    while (!q.empty()) {
        node cur = q.front();
        q.pop();
        if (cur.x == ed.x && cur.y == ed.y && cur.z == ed.z) return cur.cnt;
        for (int i = 0; i < 6; ++i) {
            node tmp;
            tmp.x = cur.x + dx[i];
            tmp.y = cur.y + dy[i];
            tmp.z = cur.z + dz[i];
            if (valid(tmp.x, tmp.y, tmp.z)) {
                tmp.cnt = cur.cnt + 1;
                q.push(tmp);
                v[tmp.x][tmp.y][tmp.z] = 1;
            }
        }
    }
    return -1;
}

int main() 
{
    while (~scanf("%d%d%d", &l, &r, &c) && l) {
        for (int i = 1; i <= l; ++i) {
            for (int j = 1; j <= r; ++j) {
                scanf("%s", mp[i][j]);
                for (int k = 0; k < c; ++k) {
                    if (mp[i][j][k] == 'S') {
                        st = node(i, j, k);
                    }
                    else if (mp[i][j][k] == 'E') {
                        ed = node(i, j, k);
                    }
                }
                getchar();
            }
        }
        int ans = bfs();
        if (ans != -1) printf("Escaped in %d minute(s).\n", ans);
        else puts("Trapped!");
    }
    return 0;
}

3. POJ3278 Catch That Cow

题目链接

题意:在线段$[0,10^5]$上,农夫 Jorn 在 N,逃跑的奶牛在 K 不动,农夫每分钟可以向左右一步或位置翻倍,求抓到奶牛最短时间。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 2e9, N = 1e5 + 5;

int n, k;
int v[N];
int dx[3] = {1, 1, 2}, dy[3] = {1, -1, 0};

void bfs() {
    memset(v, 0, sizeof(v));
    queue<int> q;
    q.push(n);
    v[n] = 1;
    while (!q.empty()) {
        int cur = q.front();
        q.pop();
        if (cur == k) return;
        for (int i = 0; i < 3; ++i) {
            int tmp = cur * dx[i] + dy[i];
            if (tmp >= 0 && tmp <= (int)1e5 && !v[tmp]) {
                q.push(tmp);
                v[tmp] = v[cur] + 1;
            }
        }
    }
}

int main()
{
    scanf("%d%d", &n, &k);
    if (n >= k) printf("%d\n", n - k);
    else {
        bfs();
        printf("%d\n", v[k] - 1);
    }
    return 0;
}

4. POJ3279 Fliptile

题目链接

题意:$M\times N$的格子有黑白两面,农夫 Jorn 的牛对格子进行翻转,每次翻转一格,相邻格子也会被翻转,求全部变为白色的最小翻转次数。

  经典二维翻转问题。直接暴力搜是$O(2^{MN})$。我们知道:翻转n次相当于n%2次,翻转顺序不影响结果。按字典序枚举第一行翻转状态,第一行确定后,只有下一行可改变上一行,最后检查最后一行即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 2e9, N = 20;

int t[N][N];
int ans[N][N], flip[N][N];
int m, n, minn = INF;
int dx[] = {0, 1, -1, 0, 0};
int dy[] = {0, 0, 0, 1, -1};

int chk(int r, int c) {
    int sum = t[r][c];
    for (int i = 0; i < 5; ++i) {
        int x = r + dx[i];
        int y = c + dy[i];
        if (x >= 1 && x <= m && y >= 1 && y <= n) {
            sum += flip[x][y];
        }
    }
    return sum & 1;
}

int solve() {
    int tmp = 0;
    for (int i = 2; i <= m; ++i)
        for (int j = 1; j <= n; ++j)
            if (chk(i - 1, j)) flip[i][j] = 1;
    for (int i = 1; i <= n; ++i)
        if (chk(m, i)) return INF;
    for (int i = 1; i <= m; ++i)
        for (int j = 1; j <= n; ++j)
            tmp += flip[i][j];
    return tmp;
}

int main() 
{
    scanf("%d%d", &m, &n);
    for (int i = 1; i <= m; ++i) {
        for (int j = 1; j <= n; ++j) {
            scanf("%d", &t[i][j]);
        }
    }
    for (int i = 0; i < (1 << n); ++i) {
        memset(flip, 0, sizeof(flip));
        for (int j = 0; j < n; ++j) {
            flip[1][n-j] = (i >> j) & 1;
        }
        int cnt = solve();
        if (cnt < minn) {
            minn = cnt;
            memcpy(ans, flip, sizeof(ans));
        }
    }
    if (minn == INF) puts("IMPOSSIBLE");
    else {
        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                printf("%d ", ans[i][j]);
            }
            puts("");
        }
    }
    return 0;
}

5. POJ1426 Find The Multiple

题目链接

题意:给定正整数 n,寻找 n 的一个非零倍数 m,m 的十进制表示每一位只含 0 或 1,假定 n 不大于 200 且 m 不多于 100 位。

  对所求数 num 从 1 开始深搜,只有两个方向 num * 10 和 num * 10 + 1,由于 long long 最大值长度为 19 位,所以数长度大于 19 位时 dfs 终止。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 2e9;

int n, flag;

void dfs(int step, ll num) {
    if (step > 19 || flag) {
        return;
    }
    if (num % n == 0) {
        flag = 1;
        printf("%lld\n", num);
        return;
    }
    dfs(step + 1, num * 10);
    dfs(step + 1, num * 10 + 1);
}

int main() 
{
    while (scanf("%d", &n) && n) {
        flag = 0;
        dfs(1, 1);
    }
    return 0;
}

6. POJ3126 Prime Path

题目链接

题意:给定两个四位素数 a, b,a 可以改变一位变成 c,但仅当 c为四位素数才能改变,求 a 变为 b 的最小次数。

  先筛出判断素数的表,再进行 bfs。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <queue>
#include <cmath>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int N = 1e5 + 5, INF = 2e9;

int isprime[N], vis[N];
int t, a, b, ans;

struct node {
    int num, step;
    node (int num, int step) : num(num), step(step) {}
};

void init() {
    memset(isprime, 1, sizeof(isprime));
    isprime[0] = isprime[1] = 0;
    for (int i = 2; i < N; ++i) {
        if (isprime[i]) {
            if (i > N / i) continue;
            for (int j = i * i; j < N; j += i) {
                isprime[j] = 0;
            }
        }
    }
}

void bfs() {
    queue<node> q;
    q.push(node(a, 0));
    vis[a] = 1;
    while (!q.empty()) {
        node cur = q.front();
        q.pop();
        if (cur.num == b) {
            ans = cur.step;
            break;
        }
        for (int i = 1; i <= 9; ++i) {
            int tmp = cur.num;
            tmp = tmp % 1000 + i * 1000;
            if (isprime[tmp] && !vis[tmp]) {
                vis[tmp] = 1;
                q.push(node(tmp, cur.step + 1));
            }
        }
        for (int i = 0; i <= 9; ++i) {
            int tmp = cur.num;
            tmp = tmp - tmp % 1000 + tmp % 100 + i * 100;
            if (isprime[tmp] && !vis[tmp]) {
                vis[tmp] = 1;
                q.push(node(tmp, cur.step + 1));
            }
        }
        for (int i = 0; i <= 9; ++i) {
            int tmp = cur.num;
            tmp = tmp - tmp % 100 + tmp % 10 + i * 10;
            if (isprime[tmp] && !vis[tmp]) {
                vis[tmp] = 1;
                q.push(node(tmp, cur.step + 1));
            }
        }
        for (int i = 0; i <= 9; ++i) {
            int tmp = cur.num;
            tmp = tmp - tmp % 10 + i;
            if (isprime[tmp] && !vis[tmp]) {
                vis[tmp] = 1;
                q.push(node(tmp, cur.step + 1));
            }
        }
    }
}

int main() 
{
    scanf("%d", &t);
    init();
    while (t--) {
        memset(vis, 0, sizeof(vis));
        ans = INF;
        scanf("%d%d", &a, &b);
        bfs();
        if (ans == INF) {
            puts("Impossible");
        }
        else printf("%d\n", ans);
    }
    return 0;
}

7. POJ3087 Shuffle’m Up

题目链接

题意:给出两个长度为 C 的字符串 s1 和 s2 和一个长度为 2C 的字符串 s12,一次操作为按先 s2 后 s1 的顺序取字符得到一个长度为 2C 的字符串 s,若 s 与 s12 相同输出操作次数,否则 s 前半部分为 s1 后半部分为 s2 继续操作,若无法成功输出 -1。

  模拟即可。当操作得到的字符串重复出现即无法成功。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <map>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 2e9;

int n, c;
string s1, s2, s12, tmp;

int search() {
    map<string, int> vis;
    int cnt = 1;
    while (tmp != s12 && !vis[tmp]) {
        vis[tmp] = 1;
        cnt++;
        s1 = tmp.substr(0, c);
        s2 = tmp.substr(c, c);
        tmp = "";
        for (int i = 0; i < c; ++i) {
            tmp += s2[i];
            tmp += s1[i];
        }
    }
    if (tmp == s12) return cnt;
    return -1;
}

int main() 
{
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) {
        scanf("%d", &c);
        cin >> s1 >> s2 >> s12;
        tmp = "";
        for (int i = 0; i < c; ++i) {
            tmp += s2[i];
            tmp += s1[i];
        }
        printf("%d %d\n", i, search());
    }
    return 0;
}

8. POJ3414 Pots

题目链接

题意:给定两个容器,分别能装$A$升和$B$升水,有以下操作:

  1. FILL(i) 用水龙头将容器$i(1\le i\le2)$装满。
  2. DROP(i) 将容器$i$排干。
  3. POUR(i,j) 将容器$i$的水倒入容器$j$,操作后容器$j$满则容器$i$有剩余,或者容器$i$空水全部倒入容器$j$。

找出次数最少的操作序列使得任何一个容器恰有$C$升水$(1\le A,B\le100,\ C\le\max(A,B))$。

  bfs优先队列分6种情况入队记录路径即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 2e9;

struct node {
    int vol1, vol2, step;
    string id, path;
    friend bool operator < (const node &x, const node &y) {
        return x.step > y.step;
    }
}now, nex;

int a, b, c, flag;
int vis[105][105];

string op[6] = {"FILL(1)", "FILL(2)", "DROP(1)", "DROP(2)", "POUR(1,2)", "POUR(2,1)"};

void bfs() {
    priority_queue<node> q;
    now.vol1 = 0, now.vol2 = 0, now.step = 0, now.id = "", now.path = "";
    vis[now.vol1][now.vol2] = 1;
    q.push(now);
    while (!q.empty()) {
        now = q.top();
        q.pop();
        if (now.vol1 == c || now.vol2 == c) {
            flag = 1;
            printf("%d\n", now.step);
            for (int i = 0; i < (int)now.path.length(); ++i) {
                cout << op[now.path[i] - '0'] << '\n';
            }
            break;
        }
        for (int i = 0; i < 6; ++i) {
            nex.id = i + '0';
            if (i == 0) {
                nex.vol1 = a;
                nex.vol2 = now.vol2;
            }
            else if (i == 1) {
                nex.vol2 = b;
                nex.vol1 = now.vol1;
            }
            else if (i == 2) {
                nex.vol1 = 0;
                nex.vol2 = now.vol2;
            }
            else if (i == 3) {
                nex.vol2 = 0;
                nex.vol1 = now.vol1;
            }
            else if (i == 4) {
                if (now.vol1 + now.vol2 > b) {
                    nex.vol1 = now.vol1 + now.vol2 - b;
                    nex.vol2 = b;
                }
                else {
                    nex.vol1 = 0;
                    nex.vol2 = now.vol1 + now.vol2;
                }
            }
            else if (i == 5) {
                if (now.vol1 + now.vol2 > a) {
                    nex.vol1 = a;
                    nex.vol2 = now.vol1 + now.vol2 - a;
                }
                else {
                    nex.vol1 = now.vol1 + now.vol2;
                    nex.vol2 = 0;
                }
            }
            if (!vis[nex.vol1][nex.vol2]) {
                vis[nex.vol1][nex.vol2] = 1;
                nex.step = now.step + 1;
                nex.path = now.path + nex.id;
                q.push(nex);
            }
        }

    }
    if (!flag) puts("impossible");
}

int main()
{
    scanf("%d%d%d", &a, &b, &c);
    bfs();
    return 0;
}

9. FZU2150 Fire Game

题目链接

题意:在一个$n\times m(1\le n,m\le10)$的板上,板上每一个格子包含草或为空,选任意两个格子放火,火每 1 秒向周围四个格子蔓延,求烧完所有草的最小时间,如果不能输出 -1。

  数据较小,枚举两个点进行 bfs 即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 2e9, N = 15;

struct point {
    int x, y, step;
    point(int x = 0, int y = 0, int step = 0) : x(x), y(y), step(step) {}
}p[105];

int t, n, m, cnt, ans;
char mp[N][N];
int vis[N][N];
int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};

bool chk() {
    for (int i = 1; i <= cnt; ++i)
        if (!vis[p[i].x][p[i].y]) return false;
    return true;
}

int bfs(point a, point b) {
    int res = 0;
    memset(vis, 0, sizeof(vis));
    queue<point> q;
    q.push(a);
    q.push(b);
    vis[a.x][a.y] = 1;
    vis[b.x][b.y] = 1;
    while (!q.empty()) {
        point cur = q.front();
        q.pop();
        for (int i = 0; i < 4; ++i) {
            point tmp = point(cur.x + dx[i], cur.y + dy[i], cur.step + 1);
            if (tmp.x >= 0 && tmp.x < n && tmp.y >= 0 && tmp.y < m && mp[tmp.x][tmp.y] == '#' && !vis[tmp.x][tmp.y]) {
                vis[tmp.x][tmp.y] = 1;
                res = max(res, tmp.step);
                q.push(tmp);
            }
        }
    }
    if (chk()) return res;
    return INF;
}

int main() 
{
    scanf("%d", &t);
    for (int i = 1; i <= t; ++i) {
        scanf("%d%d", &n, &m);
        cnt = 0;
        for (int j = 0; j < n; ++j) {
            scanf("%s", mp[j]);
            for (int k = 0; k < m; ++k) {
                if (mp[j][k] == '#') {
                    p[++cnt] = point(j, k, 0);
                }
            }
        }
        ans = INF;
        for (int j = 1; j <= cnt; ++j) {
            for (int k = j; k <= cnt; ++k) {
                ans = min(bfs(p[j], p[k]), ans);
            }
        }
        printf("Case %d: ", i);
        if (ans == INF) printf("-1\n");
        else printf("%d\n", ans);
    }
    return 0;
}

10. UVA11624 Fire!

题目链接

题意:Joe 在迷宫工作,迷宫一部分着火了,Joe 和火每分钟移动一格,火同时向四个方向蔓延,判断Joe 是否可以在火烧他之前离开迷宫,如果能输出最早时间。

  • # 一面墙
  • . 空地
  • J Joe 最初在的空地
  • F 着火的地点

  火可能不止一个,用两个队列更新 Joe 和火的状态,每秒先更新 Joe 再更新火。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 2e9, N = 1005;

struct node {
    int x, y;
    node(int x, int y) : x(x), y(y) {}
};

int t, r, c, vis[N][N];
int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};
char mp[N][N];

void bfs() {
    queue<node> jq;
    queue<node> fq;
    int time = 0;
    for (int i = 0; i < r; ++i) {
        for (int j = 0; j < c; ++j) {
            if (mp[i][j] == 'J') {
                jq.push(node(i, j));
                vis[i][j] = 1;
            }
            else if (mp[i][j] == 'F') {
                fq.push(node(i, j));
                mp[i][j] = '#';
            }
        }
    }
    while (!jq.empty()) {
        for (int i = 0, k = (int)jq.size(); i < k; ++i) {
            node now = jq.front();
            jq.pop();
            if (mp[now.x][now.y] == '#') continue;
            for (int j = 0; j < 4; ++j) {
                node nex = node(now.x + dx[j], now.y + dy[j]);
                if (nex.x >= 0 && nex.x < r && nex.y >= 0 && nex.y < c) {
                    if (mp[nex.x][nex.y] == '.' && !vis[nex.x][nex.y]) {
                        vis[nex.x][nex.y] = 1;
                        jq.push(nex);
                    }
                }
                else {
                    printf("%d\n", time + 1);
                    return;
                }
            }
        }
        for (int i = 0, k = (int)fq.size(); i < k; ++i) {
            node now = fq.front();
            fq.pop();
            for (int j = 0; j < 4; ++j) {
                node nex = node(now.x + dx[j], now.y + dy[j]);
                if (nex.x >= 0 && nex.x < r && nex.y >= 0 && nex.y < c) {
                    if (mp[nex.x][nex.y] != '#') {
                        mp[nex.x][nex.y] = '#';
                        fq.push(nex);
                    }
                }
            }
        }
        time++;
    }
    puts("IMPOSSIBLE");
}

int main() 
{
    scanf("%d", &t);
    while (t--) {
        memset(vis, 0, sizeof(vis));
        scanf("%d%d", &r, &c);
        for (int i = 0; i < r; ++i) {
            scanf("%s", mp[i]);
        }
        bfs();
    }
    return 0;
}

11. POJ3984 迷宫问题

题目链接

题意:在$5\times5$的迷宫里,1 表示墙壁,0 表示可以走的路,找出从左上角到右下角的最短路线。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 2e9;

struct node {
    int x, y;
    node (int x = 0, int y = 0) : x(x), y(y) {}
}pre[10][10];

int mp[10][10];
int vis[10][10];
int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};

void bfs() {
    memset(vis, 0, sizeof(vis));
    queue<node> q;
    q.push(node(1, 1));
    while (!q.empty()) {
        node cur = q.front();
        q.pop();
        if (cur.x == 5 && cur.y == 5) return;
        for (int i = 0; i < 4; ++i) {
            node nex = node(cur.x + dx[i], cur.y + dy[i]);
            if (nex.x > 0 && nex.x <= 5 && nex.y > 0 && nex.y <= 5 && !mp[nex.x][nex.y] && !vis[nex.x][nex.y]) {
                vis[nex.x][nex.y] = 1;
                q.push(nex);
                pre[nex.x][nex.y] = cur;
            }
        }
    }
}

void print(node cur) {
    if (cur.x == 1 && cur.y == 1) {
        printf("(0, 0)\n");
        return;
    }
    print(pre[cur.x][cur.y]);
    printf("(%d, %d)\n", cur.x - 1, cur.y - 1);
}

int main() 
{
    for (int i = 1; i <= 5; ++i) {
        for (int j = 1; j <= 5; ++j) {
            scanf("%d", &mp[i][j]);
        }
    }
    bfs();
    print(node(5, 5));
    return 0;
}

12. HDU1241 Oil Deposits

题目链接

题意:$m\times n(1\le m,n\le100)$的矩形区域,“*”表示没油,“@”表示有油,石油相邻认为是一块油田,问有多少块油田。

  dfs 深搜染色即可。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 2e9, N = 105;

int d[8][2] = {{1, 0}, {-1, 0}, {0, 1}, {0, -1}, {1, 1}, {1, -1}, {-1, 1}, {-1, -1}};
char mp[N][N];
int n, m;

void dfs(int x, int y) {
    mp[x][y] = '*';
    for (int i = 0; i < 8; ++i) {
        int tx = x + d[i][0];
        int ty = y + d[i][1];
        if (tx >= 1 && tx <= m && ty >= 1 && ty <= n && mp[tx][ty] == '@') {
            dfs(tx, ty);
        }
    }
}

int main() 
{
    while (scanf("%d%d", &m, &n) && m) {
        for (int i = 1; i <= m; ++i) {
            scanf("%s", mp[i] + 1);
        }
        int cnt = 0;
        for (int i = 1; i <= m; ++i) {
            for (int j = 1; j <= n; ++j) {
                if (mp[i][j] == '@') {
                    dfs(i, j);
                    cnt++;
                }
            }
        }
        printf("%d\n", cnt);
    }
    return 0;
}

13. HDU1495 非常可乐

题目链接

题意:给定一个 S 毫升的可乐和两个容量分别为 N 毫升和 M 毫升的杯子,在三个之间倒可乐(均没有刻度,且S ==N+M,101>S>0,N>0,M>0),问是否能平分,能请输出倒可乐的最少次数,不能输出“NO”。

  平分可乐,S 必为偶数,且为三个容器最大的两个容器平分可乐,bfs 时有 6 种倒法,可以用一些小 trick 优化。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 2e9, N = 105;

struct node {
    int v[3];
    node (int x = 0, int y = 0, int z = 0) {
        v[0] = x, v[1] = y, v[2] = z;
    }
};

int s[3], nex[3];
int step[N][N][N];

void bfs() {
    queue<node> q;
    q.push(node(s[0], 0, 0));
    step[s[0]][0][0] = 0;
    while (!q.empty()) {
        node cur = q.front();
        q.pop();
        if (cur.v[0] == cur.v[1] && cur.v[2] == 0) {
            printf("%d\n", step[cur.v[0]][cur.v[1]][cur.v[2]]);
            return;
        }
        for (int i = 0; i <= 2; ++i) {
            for (int j = 1; j <= 2; ++j) {
                for (int k = 0; k < 3; ++k) nex[k] = cur.v[k];
                int to = (i + j) % 3;
                if (cur.v[i] + cur.v[to] >= s[to]) {
                    nex[i] = cur.v[i] + cur.v[to] - s[to];
                    nex[to] = s[to];
                }
                else {
                    nex[to] = cur.v[i] + cur.v[to];
                    nex[i] = 0;
                }
                if (step[nex[0]][nex[1]][nex[2]] == -1) {
                    step[nex[0]][nex[1]][nex[2]] = step[cur.v[0]][cur.v[1]][cur.v[2]] + 1;
                    q.push(node(nex[0], nex[1], nex[2]));
                }
            }
        }
    }
    puts("NO");
}

int main() 
{
    while (scanf("%d%d%d", &s[0], &s[1], &s[2]), s[0]) {
        if (s[0] & 1) {
            puts("NO");
            continue;
        }
        if (s[1] < s[2]) swap(s[1], s[2]);
        memset(step, -1, sizeof(step));
        bfs();
    }
    return 0;
}

  也有 dalao 用数论的做法做,设两个杯子(倒出次数-倒进次数)分别为$x$和$y$,问题转化为

$$\begin{cases}\min|x|+|y|\\s.t.\quad nx+my=\displaystyle\frac{n+m}{2},\quad x,y\in \mathbb{Z},x+y\ge0\end{cases}$$

  由Bézout’s identity,

$$d=\gcd(n,m),\quad if\ \frac{n+m}{2}\%d!=0\Leftrightarrow\frac{n+m}{d}\%2!=0\Rightarrow no\ solution$$

否则令$n=ad,m=bd$,方程化为$ax+by=\displaystyle\frac{a+b}{2}$,一组简单整数解为$\displaystyle\left(\frac{1+b}{2},\frac{1-a}{2}\right)$,不定方程通解为$\displaystyle\left(\frac{1+b}{2}+kb,\frac{1-a}{2}-ka\right)$,则

$$|x|+|y|=\left|\frac{1+b}{2}+kb\right|+\left|\frac{1-a}{2}-ka\right|=\left|k+\frac{1}{2}\right|(a+b)\ge\frac{a+b}{2}$$

$\min|x|+|y|$为$\frac{a+b}{2}$,一次倒进后必有一次将瓶子中可乐倒回大瓶,倒出同理,但最后一次操作保留$\frac{n+m}{2}$毫升可乐,答案为$\frac{a+b}{2}*2-1=a+b-1=\frac{s}{d}-1$

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 2e9;

int s, n, m;

int gcd(int x, int y) { return y ? gcd(y, x % y) : x; }

int main()
{
    while (scanf("%d%d%d", &s, &n, &m), s) {
        s /= gcd(n, m);
        if (s & 1) puts("NO");
        else printf("%d\n", s - 1);
    }
    return 0;
}

14. HDU2612 Find a way

题目链接

题意:y 和 m 要去 kfc 聚餐,图中有多个 kfc,求两人到达 kfc 的最少时间和。

  y 和 m 分别 bfs 即可,注意 INF 大小,因为可能有两个人都到不了的点。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <queue>
using namespace std;

#define debug(x) cout << #x << " is " << x << endl
#define inc(i, a, b) for (int i = a; i <= b; ++i)
typedef long long ll;
const int INF = 1e9, N = 205;

struct node {
    int x, y;
    node (int x = 0, int y = 0) : x(x), y(y) {}
}Y, M;

int n, m;
char mp[N][N];
int ycost[N][N], mcost[N][N], vis[N][N];
int dx[] = {1, -1, 0, 0};
int dy[] = {0, 0, 1, -1};

void bfs(node p, int (&cost)[N][N]) {
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= m; ++j) {
            vis[i][j] = 0;
            cost[i][j] = INF;
        }
    }
    queue<node> q;
    q.push(p);
    vis[p.x][p.y] = 1;
    cost[p.x][p.y] = 0;
    while (!q.empty()) {
        node now = q.front();
        q.pop();
        for (int i = 0; i < 4; ++i) {
            node nex(now.x + dx[i], now.y + dy[i]);
            if (nex.x >= 1 && nex.x <= n && nex.y >= 1 && nex.y <= m && mp[nex.x][nex.y] != '#' && !vis[nex.x][nex.y]) {
                vis[nex.x][nex.y] = 1;
                q.push(nex);
                cost[nex.x][nex.y] = cost[now.x][now.y] + 1;
            }
        }
    }
}

int main() 
{
    while (~scanf("%d%d", &n, &m)) {
        for (int i = 1; i <= n; ++i) {
            scanf("%s", mp[i] + 1);
            for (int j = 1; j <= m; ++j) {
                if (mp[i][j] == 'Y') Y = node(i, j);
                else if (mp[i][j] == 'M') M = node(i, j);
            }
        }
        bfs(Y, ycost);
        bfs(M, mcost);
        int ans = INF;
        for (int i = 1; i <= n; ++i) {
            for (int j = 1; j <= m; ++j) {
                if (mp[i][j] == '@') {
                    ans = min(ans, ycost[i][j] + mcost[i][j]);
                }
            }
        }
        printf("%d\n", ans * 11);
    }
    return 0;
}