专题一:简单搜索、深搜、广搜

挑战程序设计竞赛(课后题难题汇总)

1. AOJ 0033 Ball(贪心)

#include<cstdio>
#include<algorithm>
using namespace std;
int T, a[15];
void solve()
{
    int x1 = a[0], x2 = -1e8;
    for(int i = 1; i < 10; i++){
        if(a[i] > x1 && a[i] > x2){
            if(a[i] - x1 > a[i] - x2){
                x2 = a[i];
            }
            else{
                x1 = a[i];
            }
        }
        else if(a[i] > x1){
            x1 = a[i];
        }
        else if(a[i] > x2){
            x2 = a[i];
        }
        else{
            printf("NO\n");
            return;
        }
    }
    printf("YES\n");
}
int main()
{
    scanf("%d", &T);
    while(T--){
        for(int i = 0; i < 10; i++){
            scanf("%d", &a[i]);
        }
        solve();
    }
    return 0;
}

(1)无需用stack,因为我们只关心最上面的两个数,因此只需用x1, x2保存最上面的两个数即可。
(2)初始化x1应为a[0],x2应初始化为-INF,不能把x2初始化为a[1],应该是直到不能往x1那一摞放小球的时候才往x2放。

2.POJ 3009 Curling 2.0 (回溯法)

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int field[25][25], dx[5] = {1, -1, 0, 0}, dy[5] = {0, 0, 1, -1};
int N, M, res, res0, sx, sy, gx, gy;
void dfs(int x, int y)
{
    if(res > 10){
        return;
    }
    for(int i = 0; i < 4; i++){
        int nx = x + dx[i], ny = y + dy[i];
        bool go = 0;
        while(1 <= nx && nx <= N && 1 <= ny && ny <= M && field[nx][ny] != 1){
            go = true;
            if(nx == gx && ny == gy){
                res0 = min(res, res0);
            }
            nx += dx[i], ny += dy[i];
        }
        if(field[nx][ny] == 1 && go){
            field[nx][ny] = 0;
            res++;
            dfs(nx - dx[i], ny - dy[i]);
            res--;
            field[nx][ny] = 1;
        }
    }

}
int main()
{
    while(scanf("%d%d", &M, &N) && N){
        memset(field, 0, sizeof(field));
        res = 1, res0 = 1e8;
        for(int i = 1; i <= N; i++){
            for(int j = 1; j <= M; j++){
                scanf("%d", &field[i][j]);
                if(field[i][j] == 2){
                    sx = i, sy = j;
                }
                else if(field[i][j] == 3){
                    gx = i, gy = j;
                }
            }
        }
        dfs(sx, sy);
        if(res0 <= 10){
            printf("%d\n", res0);
        }
        else{
            printf("%d\n", -1);
        }
    }
    return 0;
}

(1)注意这个数组下标从1开始,否则如果下标从0开始,如果一旦从左边或上边扔出墙外,下标会变为-1,然后会越界。
(2)field每组数据都一定要初始化为0,因为跑到墙外面怎么判断?一个是跳出while循环,另一个是在下面if(field[nx][ny] == 1 && go) 那里,如果不初始化,从右边和下边出了墙后,本来field对应的应该是0,但是却有可能是上一组数据的值,如果原本是1,就又进了if判断,导致出错。

3.AOJ 0558 Cheese

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int INF = 1e8;
int N, M, C, d[1005][1005], dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
typedef pair<int, int> P;
P cheese[15];
char field[1005][1005];
int bfs(int sx, int sy, int gx, int gy)
{
    for(int i = 0; i < N; i++){
        fill(d[i], d[i] + M, INF);
    }
    d[sx][sy] = 0;
    queue<P> que;
    que.push(P(sx, sy));
    while(!que.empty()){
        P p = que.front();
        que.pop();
        int x = p.first, y = p.second;
        if(x == gx && y == gy){
            return d[x][y];
        }
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i], ny = y + dy[i];
            if(0 <= nx && nx < N && 0 <= ny && ny < M && field[nx][ny] != 'X' && d[nx][ny] == INF){
                que.push(P(nx, ny));
                d[nx][ny] = d[x][y] + 1;
            }
        }
    }
}
int main()
{
    scanf("%d%d%d", &N, &M, &C);
    for(int i = 0; i < N; i++){
        scanf("%s", field[i]);
    }
    for(int i = 0; i < N; i++){
        for(int j = 0; j < M; j++){
            if(field[i][j] == 'S'){
                cheese[0] = P(i, j);
            }
            else if('0' < field[i][j] && field[i][j] <= '9'){
                cheese[field[i][j] - '0'] = P(i, j);
            }
        }
    }
    int res = 0;
    for(int i = 1; i <= C; i++){
        res += bfs(cheese[i - 1].first, cheese[i - 1].second, cheese[i].first, cheese[i].second);
    }
    printf("%d\n", res);
    return 0;
}

(1)学会把问题划分为子问题。
(1)一定要注意!虽然没有用全局变量。全局变量一定要注意!不要在后面用的时候不要再int声明,不然会屏蔽掉外部变量!
(2)此题奶酪工厂是可以直接经过的,不是没吃过的工厂就就不让过。

4.POJ 3669 Meteor Shower

(1)这次又想了一个方法,设置一个field的二维数组,记录流星砸的最早时间,永远不砸就是INF。然后算人走的时间,用d保存,一旦有field[x][y] <= d[x][y],就把d[x][y]设为2 * INF。最后,遍历两个数组,寻找field[x][y] == INF 的地方对应的d[x][y]的最小值。个人感觉这个方法更简单一些。

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int N = 305, M = 305, maxc = 50005, INF = 1e8;
int C, x[maxc], y[maxc], t[maxc], dx[4] = {0, 0, 1, -1}, dy[4] = {1, -1, 0, 0};
int field[N][M], d[N][M];
typedef pair<int, int> P;
void solve()
{
    for(int i = 0; i < N; i++){
        for(int j = 0; j < M; j++){
            field[i][j] = d[i][j] = INF;
        }
    }
    for(int i = 0; i < C; i++){
        int x1 = x[i], y1 = y[i];
        //防止重复砸。注意是找最小值,因为不一定先输入的流星砸得就早。
        field[x1][y1] = min(field[x1][y1], t[i]);
        for(int j = 0; j < 4; j++){
            int nx = x1 + dx[j], ny = y1 + dy[j];
            if(0 <= nx && nx < N && 0 <= ny && ny < M){
                field[nx][ny] = min(field[nx][ny], t[i]);
            }
        }
    }
    d[0][0] = 0;
    queue<P> que;
    que.push(P(0, 0));
    while(!que.empty()){
        P p = que.front();
        que.pop();
        int x = p.first, y = p.second;
        if(d[x][y] >= field[x][y]){
            d[x][y] = 2 * INF;
            //切记,这里别设为INF,不然无法区分不能经过这个地方与还没有到这个地方两种情况。
            continue;
        }
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i], ny = y + dy[i];
            if(0 <= nx && nx < N && 0 <= ny && ny < M && d[nx][ny] == INF){
                que.push(P(nx, ny));
                d[nx][ny] = d[x][y] + 1;
            }
        }
    }
    int res = INF;
    for(int i = 0; i < N; i++){
        for(int j = 0; j < M; j++){
            if(field[i][j] == INF){
                res = min(d[i][j], res);
            }
        }
    }
    if(res == INF){
        printf("%d\n", -1);
    }
    else{
        printf("%d\n", res);
    }
}
int main()
{
    scanf("%d", &C);
    for(int i = 0; i < C; i++){
        scanf("%d%d%d", &x[i], &y[i], &t[i]);
    }
    solve();
    return 0;
}

kuangbin系列

1.HDU 2612 Find a way

(1)这道题遍历两边城市地图就可以了,否则会超时。

#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
using namespace std;
const int INF = 1e7;
int N, M, sx1, sy1, sx2, sy2;
int dx[] = {0, 0, 1, -1}, dy[] = {1, -1, 0, 0};
char field[205][205];
int d[205][205];
typedef pair<int, int> P;
vector<P> kfc;
void bfs(int sx, int sy)
{
    for(int i = 0; i < N; i++){
        for(int j = 0; j < M; j++){
            d[i][j] = INF;
        }
    }
    d[sx][sy] = 0;
    queue<P> que;
    que.push(P(sx, sy));
    while(!que.empty()){
        P p = que.front();
        que.pop();
        int x = p.first, y = p.second;
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i], ny = y + dy[i];
            if(0 <= nx && nx < N && 0 <= ny && ny < M && field[nx][ny] != '#' && d[nx][ny] == INF){
                que.push(P(nx, ny));
                d[nx][ny] = d[x][y] + 11;
            }
        }
    }
}
void solve()
{
    int sz = kfc.size();
    vector<int> res1, res2;
    bfs(sx1, sy1);
    for(int i = 0; i < sz; i++){
        int x = kfc[i].first, y = kfc[i].second;
        res1.push_back(d[x][y]);
    }
    bfs(sx2, sy2);
    for(int i = 0; i < sz; i++){
        int x = kfc[i].first, y = kfc[i].second;
        res2.push_back(d[x][y]);
    }
    int res = 1e9;
    for(int i = 0; i < sz; i++){
        res = min(res, res1[i] + res2[i]);
    }
    printf("%d\n", res);
}
int main()
{
    while(scanf("%d%d", &N, &M) == 2){
        kfc.clear();
        for(int i = 0; i < N; i++){
            scanf("%s", &field[i]);
        }
        for(int i = 0; i < N; i++){
            for(int j = 0; j < M; j++){
                if(field[i][j] == '@'){
                    kfc.push_back(P(i, j));
                }
                else if(field[i][j] == 'Y'){
                    sx1 = i, sy1 = j;
                }
                else if(field[i][j] == 'M'){
                    sx2 = i, sy2 = j;
                }
            }
        }
        solve();
    }
    return 0;
}

2.UVA 11624 Fire!

(1)这道题很恶,题目只说有一个J,但没说只有一个F,因此有可能有多个火源。
(2)一开始我的思路是先算人到边界时间,再算每个火到边界的最短时间,若人到边界的时间小于火到边界的时间,则可以逃脱。但是这种算法会超时。
(3)因此,只能把这个地图遍历一次,先把所有的火源推入队列,然后把人推入队列,这样表明火先走,人再走。只要火到的地方就变成墙。别忘一开始把火的位置变成墙。

WA,但并不知道是哪里错了。

#include<cstdio>
#include<algorithm>
#include<queue>
#include<vector>
using namespace std;
const int INF = 1e8;
int T, N, M, dx[] = {1, -1, 0, 0}, dy[] = {0, 0, 1, -1};
int d[1005][1005], sx, sy;
char field[1005][1005];
struct P
{
    int x, y;
    bool kind;  //0 indicates John, and 1 indicates the fire;
};
vector<P> fire;
int bfs()
{
    for(int i = 0; i < N; i++){
        for(int j = 0; j < M; j++){
            d[i][j] = INF;
        }
    }
    d[sx][sy] = 0;
    queue<P> que;
    int sz = fire.size();
    for(int i = 0; i < sz; i++){
        que.push(fire[i]);
    }
    que.push({sx, sy, false});
    while(!que.empty()){
        P p = que.front();
        que.pop();
        int x = p.x, y = p.y;
        for(int i = 0; i < 4; i++){
            int nx = x + dx[i], ny = y + dy[i];
            if(0 <= nx && nx < N && 0 <= ny && ny < M && field[nx][ny] != '#' && d[nx][ny] == INF){
                if(p.kind){
                    field[nx][ny] = '#';
                    que.push({nx, ny, true});
                }
                else{
                    que.push({nx, ny, false});
                    d[nx][ny] = d[x][y] + 1;
                }
            }
        }
    }
    int res = INF;
    for(int i = 0; i < N; i++){
        res = min(res, d[i][0]);
        res = min(res, d[i][M - 1]);
    }
    for(int j = 0; j < M; j++){
        res = min(res, d[0][j]);
        res = min(res, d[N - 1][j]);
    }
    return res + 1;
}
void solve()
{
    for(int i = 0; i < N; i++){
        for(int j = 0; j < M; j++){
            if(field[i][j] == 'J'){
                sx = i, sy = j;
            }
            else if(field[i][j] == 'F'){
                fire.push_back({i, j, true});
                field[i][j] = '#';
            }
        }
    }
    int res = bfs();
    if(res >= INF){
        printf("IMPOSSIBLE\n");
    }
    else{
        printf("%d\n", res);
    }
}
int main()
{
    scanf("%d", &T);
    while(T--){
        scanf("%d%d", &N, &M);
        for(int i = 0; i < N; i++){
            scanf("%s", field[i]);
        }
        solve();
    }
    return 0;
}

cf

1.D. Solve The Maze

这道题我的一开始思路是把所有坏人的四个方向判断一下,若有好人,则结束并输出No,否则换成‘#’。然后把所有连成一起的好人换成‘.’(用深搜),这样可以减少搜索数量。最后用广搜看看每个好人到终点的距离是否为INF。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值