POJ3026 Borg Maze BFS+最小生成树

这道题其实不难,思路就是:将走过的路用直线连起来,就立刻想到了构造一颗树,而树的边权是任意两点间的最短距离,需要用bfs搜索,最后用最小生成树模板套一下就行了。但我主要遇到了三个问题,做了很久才AC:

1. 样例研究了很久没看懂,尤其是第一个,直到突然发现只有在S点和A点处才能“分队”,而不是任何位置。

2. 写bfs的时候,我一开始写的是O(n^2)的复杂度,即任意两点之间都用一次bfs,判TLE了。。然后看了别人的博客,发现只用O(n)的复杂度即可,即每个点进行一次bfs,搜索它到其余各点的最短路径;其中需要标记已经搜过的两点,否则就会加两次边。

3. 最坑的一点:输入x,y之后可能会有空格,但是没有空行。而我一开始处理的全是空行,利用cin.getline(),结果一直WA,怎么也找不到问题。。然后随手试了一下别人用的gets(),过了。。。

AC代码如下:

#include<cstdio>
#include<cstring>
#include<cmath>
#include<iostream>
#include<algorithm>
#include<vector>
#include<queue>
#include<map>
using namespace std;
const int INF=0x3f3f3f3f;
#define pb push_back
#define mkp make_pair
typedef pair<int,int>pp;

const int MAX=55;
const int MAXN=106;
const int MAXM=MAXN*MAXN/2;
int x,y;//长,宽
char s[MAX][MAX];
int n,m;//点,边
vector<pp>vt;//点
map<pp,int>mp;//点的编号
bool sign[MAXN][MAXN];//标记两点间是否有边
struct Edge
{
    int u,v,w;
}e[MAXM];
void init()
{
    memset(s,'\0',sizeof(s));
    memset(e,0,sizeof(e));
    vt.clear();mp.clear();
    n=0;m=0;x=0,y=0;
    memset(sign,false,sizeof(sign));
}

int f[MAXN];
int Find(int x)
{
    if(x==f[x])
        return x;
    else
        return Find(f[x]);
}
bool cmp(struct Edge a,struct Edge b)
{
    return a.w<b.w;
}
void kruskal()
{
    for(int i=0;i<n;i++)
        f[i]=i;
    sort(e,e+m,cmp);
    int cnt=0,ans=0;
    for(int i=0;i<m;i++)
    {
        int u=e[i].u,v=e[i].v,w=e[i].w;
        int u0=Find(u),v0=Find(v);
        if(u0!=v0)
        {
            f[v0]=u0;
            cnt++;
            ans+=w;
        }
        if(cnt==n-1) break;
    }
    printf("%d\n",ans);
}

void bfs(pp x); //某点到其他点之间的距离
int main()
{
    int t;
    scanf("%d",&t);
    while(t--)
    {
        init();
        scanf("%d%d",&x,&y);
        /*while(cin.getline(s[0],MAX))
            if(s[0][0]!='\n'&&s[0][0]!='\0') //处理空行
                break;
        //A null character('\0') is automatically appended even if an empty string is extracted.
        for(int i=1;i<y;i++)
            cin.getline(s[i],MAX);*/
        gets(s[0]);
        for(int i=0;i<y;i++)
            gets(s[i]);
        /*for(int i=0;i<y;i++)
            cout<<"i="<<i<<" "<<s[i]<<endl;*/
        for(int i=0;i<y;i++)
            for(int j=0;j<x;j++)
                if(s[i][j]=='S'||s[i][j]=='A')
                {
                    pp tmp=mkp(i,j);
                    vt.pb(tmp);
                    mp[tmp]=n+1;//从0开始在判断访问时会出bug
                    n++;
                }
        for(int i=0;i<n;i++)
            bfs(vt[i]);
        //cout<<n<<" "<<m<<endl;
        kruskal();
    }
    return 0;
}

int vis[MAX][MAX];//标记是否访问且记录步数
int d[4][2]={{0,-1},{0,1},{1,0},{-1,0}};
void bfs(pp st) //某点到其他点之间的距离
{
    queue<pp>que;
    while(!que.empty())
        que.pop();
    memset(vis,0,sizeof(vis));
    vis[st.first][st.second]=1;//0可能会出bug
    que.push(st);
    while(!que.empty())
    {
        pp k=que.front();
        que.pop();
        int dis=vis[k.first][k.second];
        for(int i=0;i<4;i++)
        {
            int a=k.first+d[i][0],b=k.second+d[i][1];
            if(a>=0&&a<y&&b>=0&&b<x&&!vis[a][b]&&s[a][b]!='#')
            {
                pp t=mkp(a,b);
                if(mp[t]&&sign[mp[st]][mp[t]]==false&&sign[mp[t]][mp[st]]==false)//加边
                {
                    e[m].u=mp[st]-1;e[m].v=mp[t]-1;//顶点从0开始标号
                    e[m].w=dis;//因为起点vis=1,所以此处不需要+1
                    m++;
                    sign[mp[st]][mp[t]]=true;sign[mp[t]][mp[st]]=true;
                    //注意此处不需要break!!
                }
                vis[a][b]=dis+1;
                que.push(t);
            }
        }
    }
}

大三上的比赛结束后,这两年我就没怎么刷题了,脑速手速也基本“残废”。也许以后更有没时间,但希望能保持多敲代码的习惯吧。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值