codeforces#375(div.2)723D - Lakes in Berland dfs+bfs

题意:给你一张n×m的图,点代表水,星代表地,当水连在一块时,称为湖,但是,若湖中有水在边界上,则不算它是湖,现在要求你去掉x个湖,并用地将其填上,使原图剩下k个湖,输出你最少需要多少个单位的地,并且输出你填补过的图。


比赛的时候没过,好菜呀。


思路:首先定义两个数组bfs_vis,dfs_vis,分别记录bfs和dfs的访问状况,定义flag,true代表一块区域属于湖,false代表一块区域不属于湖,vector[top]记录第top个湖内的点的情况,top初始化为0。

然后说下dfs的思路,从一个'.'点出发,并且只走'.',若其走到了边界上,则标记flag为false,此时并不结束dfs,而是继续走,将这一整块水域全部走完,并且dfs_vis都标记为true代表已经走过。相反,若走不到边界上,则一定能走完这个水域,flag为true,在dfs的同时,将走过的每一个点都装入vec[top]。当dfs完成后,若flag为false,则清空vec[top],否则top++,进入下个vec[top]记录新的湖。

然后说bfs,就是从(1,1)开始遍历,若找到一个点'.',并且其dfs_vis为false的话,说明它没有在dfs中被访问过,则对其进行dfs,当dfs完成后,若flag为true,说明这个点所在水域为湖,则记录它的大小和序号。

最后只要对这些湖的大小进行升序排序,并且相应的将其中的点填上就行了。


为什么这么强调dfs_vis呢,因为我就坑在这啦,最开始的时候,我在dfs中一旦发现某个点走到了边界就立即停止dfs并返回false,这样出现的问题就是当一个水域只有一个点在边界的时候,假如我们第一次就dfs到了改点,那么就相当于将其变成了陆地。则其剩下的水域在下一次dfs的时候就会被判定为一个湖。


代码:

#include<bits/stdc++.h>
#define MEM(a,x) memset(a,x,sizeof(a));
#define MEMINF(a) memset(a,0x3f,sizeof(a));
using namespace std;
typedef long long LL;
const int MAXN=4000;
const int INF=0x3f3f3f3f;
const int MOD=1000000007;
int n,m,k;
char mp[MAXN][MAXN];
int dx[]={0,0,1,-1};
int dy[]={1,-1,0,0};
int top;
struct node {
  int x,y;
};
bool bfs_vis[MAXN][MAXN];
bool dfs_vis[MAXN][MAXN];
bool cmp(node a,node b) {
  return a.x<b.x;
}

vector<node>que;
queue<node>q;
vector<node> vec[MAXN];
bool flag;
void dfs(node u) {
  vec[top].push_back(u);
  dfs_vis[u.x][u.y]=1;
  if (u.x==1||u.x==n||u.y==1||u.y==m) flag=false;
  for (int i=0; i<4; ++i) {
    node v=u;
    v.x+=dx[i];
    v.y+=dy[i];
    if (dfs_vis[v.x][v.y])continue;
    if (mp[v.x][v.y]!='.') continue;
    //printf("%d %d\n",v.x,v.y);
    if (v.x==1||v.x==n||v.y==1||v.y==m) flag=false;
    dfs(v);
  }
}

void bfs(){
  node u;
  u.x=1,u.y=1;
  q.push(u);
  bfs_vis[1][1]=1;
  MEM(bfs_vis,0);
  while(!q.empty()) {
    node no=q.front();
    q.pop();
    //printf("m %c %d\n",mp[4][3],!dfs_vis[4][3]);
    if (mp[no.x][no.y]=='.'&&!dfs_vis[no.x][no.y]) {
      flag=true;
      //puts("-----");
      dfs(no);
      if (flag) {
       //printf("no:%d %d\n",no.x,no.y);
        //puts("*****");
        node lal;
        lal.x=vec[top].size();
        lal.y=top;
        que.push_back(lal);
        top++;
      }
      else {
        vec[top].clear();
      }
    }
    for (int i=0; i<4; ++i) {
      node v=no;
      v.x+=dx[i];
      v.y+=dy[i];
      if (bfs_vis[v.x][v.y])continue;
      if (v.x<1||v.x>n||v.y<1||v.y>m) continue;
      q.push(v);
      bfs_vis[v.x][v.y]=1;
    }
  }
}
      
int main() {
  scanf("%d %d %d",&n,&m,&k);
  top=0;
  for (int i=1; i<=n; ++i) {
    scanf("%s",mp[i]+1);
  }

  bfs();
  //printf("MAP%ld\n",que.size());
  int sum=0;
  sort(que.begin(),que.end(),cmp);
  for (auto i=0; i<top-k; ++i) {
    sum+=que[i].x;
    int num=que[i].y;
    for (int it=0;it<vec[num].size(); ++it) {
      int x=vec[num][it].x;
      int y=vec[num][it].y;
     mp[x][y]='*';
    }
  }

  printf("%d\n",sum);
  for (int i=1; i<=n; ++i) 
   puts(mp[i]+1);

}




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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值