考试题目讲解-【第3题】最少连通代价

最少连通代价(mincon.cpp)


Description
在一个 N 行 M 列的字符网格上,恰好有 2 个彼此分开的连通块。每个连通块的一个格点与它的上、下、左、右的格子连通。如下图所示:
这里写图片描述
现在要把这 2 个连通块连通,求最少需要把几个’.’转变成’X’。上图的例子中, 最少只需要把 3 个’.’转变成’X’。下图用’*’表示转化为’X’的格点。
这里写图片描述

Input
第 1 行:2 个整数 N 和 M(1<=N,M<=50)
接下来 N 行,每行 M 个字符,’X’表示属于某个连通块的格点,’.’表示不属于某个连通块的格点
Output
第 1 行:1 个整数,表示最少需要把几个’.’转变成’X’

Sample Input

6 16
................
..XXXX....XXX...
...XXXX....XX...
.XXXX......XXX..
........XXXXX...
.........XXX....

Sample Output

3

题目解析
这道题是一道搜索题,或者说是一道“半搜索题”。作者的做法和其他同学的做法有些出入,先讲其他同学的做法吧。

他们的做法是纯搜索,并且深度优先搜索和广度优先搜索都用到了,这也应该是大多数同学们的想法。
首先是输入。输入的部分可以采取优化,由于不需要在输入时对某一元素进行单独操作,所以我们没有必要用“%c”一个字符一个字符地读入,而是直接用“%s”读入一整行,还省去了对换行符的处理。但是对于喜欢从1开始计数的同学来说,这个方法似乎不可行,实际上“%s”也可以从1开始读入字符串的,即scanf("%s",s+1);
题目中明显给出了输入数据包含两个连通块,但是并没有用任何方式标明某个块是属于哪一个连通块。于是输入后的第一个处理便是——用字符区分出两个连通块。我们可以像城堡问题(参考:OpenJudge 1817城堡问题)一样查找连通块,并作标记。其实并不用把两个连通块都处理成其他字符,我们只需要让空白、连通块1和连通块2有区别就行了,也就是只需要处理1个连通块。
然后再用2重for循环遍历整个矩阵,一旦找到一个属于连通块1的元素就从那个位置开始进行广度优先搜索,目的是找到离该位置最近的属于连通块2的位置的距离。答案用ans储存。
看明白了他们的代码的作者想问——一个深度优先搜索加上不知多少个广度优先搜索真的不会超时吗?

还是看看作者的做法吧。作者之所以说“有很大出入”是因为我的代码除了输入,与其他同学的几乎完全不一样。首先作者也需要处理2个连通块,但是,作者选择用2个深度优先搜索分别找到连通块1和连通块2所包含的点的坐标。当然这需要储存,作者用了vector动态数组vector<Da> vec[2],vec[0]表示第一个连通块,vec[1]表示第二个连通块),它的基本类型是结构体Da,Da的两个成员变量x,y是坐标
找到所有的块过后,我们可以用2重for循环找到连通块1和连通块2中的块所有的搭配,并用ans储存它们之间距离的最小值。这里并不需要用广度优先搜索找到最短距离,而是用了一个求曼哈顿距离的公式。即(i,j)到(m,n)的曼哈顿距离=|i-m|+|j-n|。C++里绝对值的函数是fabs(建议不要用abs),在cmath库里。但是这个公式求的是两个点的曼哈顿距离,不会出现重叠面积的情况,而现在是两个块——在运用时会重叠一块面积,于是要将所得结果减1

啊哈?你们觉得哪种方法简单呢?


题外话
这次考试我们和高中的新生一起考,十分的兴奋。说考递归和二分查找,作者抓紧复习了一中午的二分查找,结果没有一道题考这个知识点。不过也好,作者没有发挥失常,3道题全部AC,终于体验了一把AK的爽。不久就是NOI大赛了,有没有一些同学和我一样,也在期待11月呢?


程序样例
真的不会超时吗?同学们的做法——深度优先搜索+广度优先搜索

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,pre[1000000],a[1000000],b[1000000],minn,ans=1e10;
int x[4]={1,-1,0,0},y[4]={0,0,1,-1};
char map[100][100];
bool mark[100][100];
bool check(int s,int t)
{
    if(s&&t&&s<=n&&t<=m&&!mark[s][t]&&map[s][t]!='S') return 1;
    return 0;
}
void fun(int d)
{
    minn++;
    if(pre[d]) fun(pre[d]);
}
void bfs(int r,int c)
{
    memset(mark,0,sizeof(mark));
    minn=0;
    int head=0,tail=1;
    int nextr,nextc;
    mark[r][c]=1;
    pre[1]=0;
    a[1]=r;
    b[1]=c;
    while(head!=tail)
    {
        head++;
        for(int i=0;i<4;i++)
        {
            nextr=a[head]+x[i];
            nextc=b[head]+y[i];
            if(check(nextr,nextc))
            {
                tail++;
                a[tail]=nextr;
                b[tail]=nextc;
                mark[nextr][nextc]=1;
                pre[tail]=head;
                if(map[nextr][nextc]=='X')
                {
                    fun(tail);
                    ans=min(minn,ans);
                }
            }
        }
    }
}
void dfs(int r,int c)
{
    for(int i=0;i<4;i++)
        if(check(r+x[i],c+y[i])&&map[r+x[i]][c+y[i]]!='.')
        {
            mark[r+x[i]][c+y[i]]=1;
            map[r+x[i]][c+y[i]]='S';
            dfs(r+x[i],c+y[i]);
            mark[r+x[i]][c+y[i]]=0;
        }
}
void f()
{
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(map[i][j]=='X')
            {
                dfs(i,j);
                map[i][j]='S';
                return ;
            }
}
int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%s",map[i]+1);
    bool flag=1;
    f();
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            if(map[i][j]=='S')
                bfs(i,j);
    printf("%d",ans-2);
}

作者觉得还是自己的好点。深度优先搜索+曼哈顿距离

/*Lucky_Glass*/
#include<cstdio>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
struct Da{int x,y;} D;
vector<Da> vec[2];
char map[55][55];
int F[4][2]={{1,0},{-1,0},{0,1},{0,-1}},r,c,S;
void flag(int x,int y)
{
    D.x=x;D.y=y;
    vec[S].push_back(D);
    map[x][y]='.';
    for(int i=0;i<4;i++)
    {
        int sx=x+F[i][0],sy=y+F[i][1];
        if(0<=sx && sx<r && 0<=y && y<c && map[sx][sy]=='X')
            flag(sx,sy);
    }
}
int main()
{
    scanf("%d%d",&r,&c);
    for(int i=0;i<r;i++)
        scanf("%s",map[i]);
    for(int i=0;i<r;i++)
        for(int j=0;j<c;j++)
            if(map[i][j]=='X')
            {
                flag(i,j);
                S++;
            }
    int la=vec[0].size(),lb=vec[1].size(),ans=1e8;
    for(int i=0;i<la;i++)
        for(int j=0;j<lb;j++)
            ans=min(ans,int(fabs(vec[0][i].x-vec[1][j].x)+fabs(vec[0][i].y-vec[1][j].y)-1));
    printf("%d\n",ans);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值