poj3026 bfs+最小生成树

题目链接在这里

题目大意:

一群人从起点S开始找外星人,#代表墙壁,A代表外星人。问把所有的外星人找到最少需要多少步。

思路分析:

对每一个为A或者S的点进行bfs,算出来和其它点的最短距离。然后把这些两两之间的距离当成边,构造最小生成树。

注意!!!

1.题目先输入了列数再输入了行数。

2.将列数行数输入完之后要gets一下空格!我用的getchar()没用,wa掉了。

3.数组开大一点!我开的55 * 55没用。。。改成105 * 105才过的。

 

这次参考了金海峰的代码风格,感觉看起来挺简约的。

代码中有kruskal和prim两种算法,想试另外一种算法的,就直接将输出的prim()改成kruskal()就好了。

代码如下:

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#include <queue>
using namespace std;
const int INF = 0x3f3f3f3f;
const int MaxN = 105;
bool map[MaxN][MaxN];
int dis[MaxN][MaxN];
int cost[MaxN][MaxN];
char str[MaxN][MaxN];
int t, n, m, cnt;

int mx[4] = {0, 0, -1, 1};
int my[4] = {-1, 1, 0, 0};

struct Point{
    int x, y;
}p[MaxN * MaxN];

struct Edge{
    int x, y, val;
    bool operator < (const Edge &e) const{
        return val > e.val;
    }
};

void input(){
    scanf("%d %d", &m, &n);
    gets(str[0]);
    cnt = 1;
    for(int i = 0; i < n; ++i){
        gets(str[i]);
        for(int j = 0; j < m; ++j){
            if(str[i][j] == '#')
                map[i][j] = false;
            else{
                map[i][j] = true;
                if(str[i][j] == 'A')
                    p[cnt++] = Point{i, j};
                if(str[i][j] == 'S')
                    p[0] = Point{i, j};
            }
        }
    }
}

bool judge(int x, int y){
    return x >= 0 && x < n && y >= 0 && y < m && map[x][y] && dis[x][y] == -1;
}

void bfs(const Point &p){
    memset(dis, -1, sizeof(dis));
    queue<Point> que;
    que.push(p);
    dis[p.x][p.y] = 0;
    while(!que.empty()){
        Point p1 = que.front();
        que.pop();
        for(int i = 0; i < 4; ++i){
            int nx = p1.x + mx[i];
            int ny = p1.y + my[i];
            if(judge(nx, ny)){
                //printf("nx:%d, ny:%d\n", nx, ny);
                que.push(Point{nx, ny});
                dis[nx][ny] = dis[p1.x][p1.y] + 1;
            }
        }
    }
}

void make(){
    for(int i = 0; i < cnt; ++i){
        bfs(p[i]);
        for(int j = 0; j < cnt; ++j){
            cost[i][j] = dis[p[j].x][p[j].y];
            //printf("i:%d, j:%d, cost:%d\n", i, j, cost[i][j]);
        }
    }
}


int prim(){
    bool vis[MaxN * MaxN];
    int lowc[MaxN * MaxN];
    int ans = 0;
    memset(vis, false, sizeof(vis));
    vis[0] = true;
    for(int i = 1; i < cnt; ++i)
        lowc[i] = cost[0][i];
    for(int i = 0; i < cnt - 1; ++i){
        int Minc = INF;
        int p = -1;
        for(int j = 0; j < cnt; ++j){
            if(Minc > lowc[j] && !vis[j]){
                Minc = lowc[j];
                p = j;
            }
        }
        if(Minc == INF) return -1;
        //printf("p:%d, cost:%d\n", p, Minc);
        vis[p] = true;
        ans += Minc;
        for(int j = 0; j < cnt; ++j)
            if(!vis[j] && lowc[j] > cost[p][j])
                lowc[j] = cost[p][j];
    }
    return ans;
}


int par[MaxN * MaxN], r[MaxN * MaxN];

int Find(int x){
    if(x == par[x]) return x;
    return par[x] = Find(par[x]);
}

void unite(int x, int y){
    x = Find(x);
    y = Find(y);
    if(x == y)  return;
    if(r[x] < r[y]) par[x] = y;
    else{
        par[y] = x;
        if(r[x] == r[y])    ++r[x];
    }
}

bool check(int x, int y){
    return Find(x) == Find(y);
}

int kruskal(){
    for(int i = 0; i <= cnt; ++i){
        par[i] = i;
        r[i] = 0;
    }
    priority_queue<Edge> que;
    for(int i = 0; i < cnt; ++i){
        for(int j = 0; j < i; ++j){
            que.push(Edge{i, j, cost[i][j]});
        }
    }

    int ans = 0;
    while(!que.empty()){
        Edge e = que.top();
        que.pop();
        if(check(e.x, e.y)) continue;
        unite(e.x, e.y);
        ans += e.val;
    }
    int cc = 0;
    for(int i = 0; i < cnt; ++i)
        if(par[i] == i) ++cc;
    if(cc != 1) return -1;
    return ans;
}

int main(){
    scanf("%d", &t);
    while(t--){
        input();
        make();
        printf("%d\n", prim());
    }
    return 0;
}

 

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值