【八中】最少连通代价(mincon.cpp)

来源:互联网 发布:pptv网络电视怎么用 编辑:程序博客网 时间:2024/05/01 07:37

3.最少连通代价(mincon.cpp)

声明:本博客为博主原创未经允许,不得转载

在一个 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

解析:

在完成此题时,我们可以用深搜+广搜,也或者只用一个深搜。但在此之前,你必须明白曼哈顿距离。如下图:
原创
如果你明白了,那你也就应该知道了,为什么可以只用一个深搜了。

代码大解析:

(1)深搜+广搜

#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);}

(2)深搜(平民版)

#include <cstdio>#define MAXN 55#define MAXM 55char G[MAXN][MAXM];int N, M;void dfs(int r, int c, char ch)//深搜,把第一个连通块变为1,把第二个连通块变为2{  if (G[r][c]!='X') return;//当不等于'X'时,退出  G[r][c] = ch;//模仿走路  if (r>0) dfs(r-1,c,ch);  if (c>0) dfs(r,c-1,ch);  if (r<N-1) dfs(r+1,c,ch);  if (c<M-1) dfs(r,c+1,ch);}int abs(int x)//取绝对值{  if (x>=0) return x;  return -x;}int mindist(void){  int r1, r2, c1, c2, min=MAXN+MAXM;//min=110  for (r1=0; r1<N; r1++)    for (c1=0; c1<M; c1++)      if (G[r1][c1]=='1')    for (r2=0; r2<N; r2++)      for (c2=0; c2<M; c2++)        if (G[r2][c2]=='2')//保证了r1,r2,c1,c2为连通块的坐标          if (abs(r1-r2) + abs(c1-c2) < min)//取最小路径             min = abs(r1-r2) + abs(c1-c2);//(曼哈顿距离)  return min - 1;}int main(void){  int r, c;  char ch='0';  scanf ("%d %d", &N, &M);  for (r=0; r<N; r++)    scanf ("%s", &G[r]);  for (r=0; r<N; r++)    for (c=0; c<M; c++)      if (G[r][c] == 'X') //如果是'X'的话,就深搜         dfs(r,c,++ch);  printf ("%d\n", mindist());//找到最小路径  return 0;}

(3)深搜(大神版)

#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);//把D.X和D.y压进ves数组里    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]);//因为没有打‘ ’,所以用‘%s’    for(int i=0;i<r;i++)        for(int j=0;j<c;j++)            if(map[i][j]=='X')            /*            当第一次找到它的时候,就深搜,并把它们都变成点点,            那当第二次找到它的时候,就一定是另外一个连通块。            */            {                flag(i,j);//深搜                S++;//当s==0时,就一定为第一个;当s==1时,就为第二个(连通块)。            }    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;}

思想大开发:

在完成此题时,还有另外一种方法如下图所示:

这里写图片描述

如果有兴趣的同学,可以完成这种做法,然后发至评论。

原创粉丝点击