算法学习之——种子填充法(flood_fill)

算法学习之——种子填充法(flood_fill)
                                                MPS
【讲义 Define】
    对于一张图,我们需要寻找极大连通子图,那么就用到了flood_fill
    flood_fill和BFS类似,Flood_fill依赖于栈(基奠是DFS),BFS依赖于队列
    这里着重介绍Flood_fill

    深搜最容易写,但它需要一个栈。搜索显式图没问题,而对于隐式图,栈可能就存不下了。

    广搜稍微好一点,不过也存在问题。搜索大的图它的队列有可能存不下。深搜和广搜时间复杂度均为O(N+M)。其中,N为结点数,M为边数。

    广度扫描需要的额外空间很少,或者可以说根本不要额外空间,但是它很慢。时间复杂度是O(N^2+M)。

(实际应用中,我们一般写的是DFS,因为快。空间不是问题,DFS可改用非递归的栈操作完成。)

【写法】
    void flood_fill(int x,int y,int id){
       if(越界或者已经被填充)return;
       vis[x][y]=id; //填充这个格子
       拓展能达到的地方
   }

【例题】

 

1.送给圣诞夜的极光 Vijos P1051

   

描述

圣诞老人回到了北极圣诞区,已经快到12点了。也就是说极光表演要开始了。这里的极光不是极地特有的自然极光景象。而是圣诞老人主持的人造极光。

轰隆隆……烟花响起(来自中国的浏阳花炮之乡)。接下来就是极光表演了。

人造极光其实就是空中的一幅幅n*m的点阵图像。只是因为特别明亮而吸引了很多很多小精灵的目光,也成为了圣诞夜最美丽的一刻。

然而在每幅n*m的点阵图像中,每一个点只有发光和不发光两种状态。对于所有的发光的点,在空中就形成了美丽的图画。而这个图画是以若干个(s个)图案组成的。对于图案,圣诞老人有着严格的定义:对于两个发光的点,如果他们的曼哈顿距离(对于A(x1,y1)和B(x2,y2),A和B之间的曼哈顿距离为|x1-x2|+|y1-y2|)小于等于2。那么这两个点就属于一个图案……
小精灵们一边欣赏着极光,一边数着每一幅极光图像中的图案数。伴着歌声和舞蹈,度过了美丽的圣诞之夜。^_^

格式

输入格式

第一行,两个数n和m。

接下来一共n行,每行m个字符。对于第i行第j个字符,如果其为“-”,那么表示该点不发光,如果其为“#”,那么表示该点发光。不可能出现其他的字符。

输出格式

第一行,一个数s。

样例1

样例输入1

19 48
------------------------------------------------
---####-----#-----#----------------------####---
--######----#-----#---------------------######--
-########--#-#---#-#####--#-##-##---#--########-
-###--###--#-#---#-#----#-##-##--#--#--###--###-
-###--###--#--#-#--######-#--#---#-#---###--###-
-########--#--#-#--#------#--#----##---########-
--######---#---#---######-#--#-----#----######--
---####----------------------------#-----####---
----------------------------------#-------------
------------------------------------------------
---###--#--------#------#-----------------------
--#---#-#---------------#-----------------------
-#------#-##--#-##--##-###-#-##-###--###-#--##--
-#------##--#-##-#-#----#--##--#---##---##-#----
-#------#---#-#--#--#---#--#---#---##----#--#---
--#---#-#---#-#--#---#--#--#---#---##---##---#--
---###--#---#-#--#-##---#--#---#---#-###-#-##---
------------------------------------------------

样例输出1

4

限制

各个测试点1s

提示

1<=n,m<=100

分析:

  实际上,题目让我们求图案中的联通子图的个数(可能有点不标准,但通俗易懂),这样,flood_fill就派上了用场

  我们发现,每次拓展的位置是如下的(假设初始点为O,可拓展的地方为*)

     *

    ***

  **O**

    ***

     *

  这样,算法就出来了

代码:

  

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

const int MaxN=1001;

int a[MaxN][MaxN];
char c;
int n,m,id;
int x,y;

void init(){
	memset(a,-1,sizeof(a));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;i++)
	  for(int j=1;j<=m;j++)
	  {
	  	cin>>c;
	  	if(c=='#')a[i][j]=0;
	  }
}

bool ok(){
	for(int i=1;i<=n;i++)
	  for(int j=1;j<=m;j++)
	    if(!a[i][j]){
	    	x=i;
	    	y=j;
	    	return false;
		}
	return true;
}

void flood_fill(int sx,int sy){
	a[sx][sy]=id;
	/*
	拓展方式:
	     * 
	    ***
	   **o**
	    ***
	     *
	*/
	if(sx>1 && !a[sx-1][sy])flood_fill(sx-1,sy);
	if(sx>1 && sy>1 && !a[sx-1][sy-1])flood_fill(sx-1,sy-1);
	if(sx>1 && sy1 && !a[sx][sy-1])flood_fill(sx,sy-1);
	if(sy1 && !a[sx+1][sy-1])flood_fill(sx+1,sy-1);
	if(sx2 && !a[sx-2][sy])flood_fill(sx-2,sy);
	if(sy>2 && !a[sx][sy-2])flood_fill(sx,sy-2);
	if(sy


2.城堡  IOI1994 Day1

   

描述

以一个几乎超乎想像的运气,农民约翰在他的生日收到了一张爱尔兰博彩的奖券。
这一张奖券成为了唯一中奖的奖券。
农民约翰嬴得爱尔兰的乡下地方的一个传说中的城堡。
吹牛在他们威斯康辛州不算什么,农民约翰想告诉他的牛所有有关城堡的事。
他想知道城堡有多少房间,而且最大的房间有多大。
事实上,他想去掉一面墙来制造一个更大的房间。


你的任务是帮助农民约翰去了解正确房间数目和大小。
城堡的平面图被分为 M(wide)*N(1 <=M,N<=50)个小正方形。
每个这样的小正方形有0到4面墙。
城堡在它的外部边缘总是有墙壁的,好遮挡风雨。


考虑这注解了一个城堡的平面图:
     1   2   3   4   5   6   7
   #############################
 1 #   |   #   |   #   |   |   #
   #####---#####---#---#####---#   
 2 #   #   |   #   #   #   #   #
   #---#####---#####---#####---#
 3 #   |   |   #   #   #   #   #   
   #---#########---#####---#---#
 4 # ->#   |   |   |   |   #   #   
   ############################# 
(图片看不清可放至word或者txt查看)

# =墙壁 -,    | = 没有墙壁
-> =移除这面墙能使得到的新房间最大




例子的城堡的大小是7 x 4。


一个 "房间"是平面图上有连接的"小正方形"的集合。
这个平面图包含五个房间。(它们的大小是9,7,3,1, 和 8 排列没有特别的顺序)。


移除被箭作记号的墙壁来合并两个房间来制造最大的可能房间(移除一面墙所能产生的)。
城堡总是至少有二个房间并且总是有一面墙壁以可能被移除

格式

输入格式

地图以一个表格来储存,每个数字描述一个小正方形,N行每行M个数来描述这个平面图。
输入顺序符合那个在上面例子的编号方式。
每个描述小正方形的数字说明小正方形的四面的墙的分布情况,它是下面4个数的和

1: 在西面有墙 
2: 在北面有墙 
4: 在东面有墙 
8: 在南面有墙 

内部的墙壁是会被定义两次;小正方形(1,1)南面的墙也被指出是小正方形(2,1)北面的墙。

第 1 行: 二个被空格分开的整数: M 和 N 
第 2 到 N+1 行:  M x N 个整数,每行M个。 

输出格式

第 1 行:  城堡的房间数目。 
第 2 行:  最大的房间的大小 
第 3 行:  移除一面墙能得到的最大的房间的大小  
第 4 行:  移除哪面墙 

选择最佳的墙来移除,(选择最靠西的,如果仍然不能确定,再选择最靠南的)
(【原文】Choose the optimal wall to remove from the set of optimal walls by choosing the wall farther to the west (and then, if still tied, farthest to the south).)
墙壁由它在相邻的小正方形的西部或南方来命名

样例1

样例输入1

7 4
11 6 11 6 3 10 6
7 9 6 13 5 15 5
1 10 12 7 13 7 5
13 11 10 8 10 12 13

样例输出1

5
9
16
4 1 E

限制

各个测试点1s

提示

1<=n,m<=50

分析:

  对于前两问,还是求极大连通子图,统计最大面积以及这张图上有多少块联通子图

   对于后两问,我们在求第一二问时,弄个vis数组表示(i,j)这个房间属于第几块联通子图,cnt表示第i块联通子图的数量

   然后逐一枚举房间,每个房间考虑北方和西方,但是这里有个坑的地方:最后一句话有点绕

   意思就是,枚举房间时,优先考虑纵坐标小的,如果不行,则考虑横坐标大的

代码:

#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

const int MaxN=100;

bool room[MaxN][MaxN][4];//0->north 1->south 2->west 3->earth
int vis[MaxN][MaxN];
int n,m,x,id,sx,sy;
int cnt[MaxN*MaxN],MAX;//cnt统计房间id的大小 ,MAX则统计最大 

void get_castle(){
  int i,j;
  memset(room,false,sizeof(room));
  for(i=1;i<=n;i++)
    for(j=1;j<=m;j++){//预处理出整个城堡 
      scanf("%d",&x); 
      if(x==1)room[i][j-1][3]=room[i][j][2]=true;//西面 
      if(x==2)room[i-1][j][1]=room[i][j][0]=true;//北面 
      if(x==3)room[i-1][j][1]=room[i][j][0]=room[i][j-1][3]=room[i][j][2]=true;//西面和北面有墙 
      if(x==4)room[i][j+1][2]=room[i][j][3]=true;//东面 
      if(x==5)room[i][j-1][3]=room[i][j][2]=room[i][j+1][2]=room[i][j][3]=true;//西面东面有墙  
      if(x==6)room[i-1][j][1]=room[i][j][0]=room[i][j+1][2]=room[i][j][3]=true;//北、东
      if(x==7)room[i-1][j][1]=room[i][j][0]=room[i][j-1][3]=room[i][j][2]=room[i][j+1][2]=room[i][j][3]=true;//北面、西面、东面有墙
      if(x==8)room[i+1][j][0]=room[i][j][1]=true;//南面 
	  if(x==9)room[i+1][j][0]=room[i][j][1]=room[i][j-1][3]=room[i][j][2]=true;//南、西 
	  if(x==10)room[i-1][j][1]=room[i][j][0]=room[i+1][j][0]=room[i][j][1]=true;//北、南 
      if(x==11)room[i][j-1][3]=room[i][j][2]=room[i-1][j][1]=room[i][j][0]=room[i+1][j][0]=room[i][j][1]=true;//西、北、南 
      if(x==12)room[i][j+1][2]=room[i][j][3]=room[i+1][j][0]=room[i][j][1]=true;//东、南 
	  if(x==13)room[i][j+1][2]=room[i][j][3]=room[i][j-1][3]=room[i][j][2]=room[i+1][j][0]=room[i][j][1]=true;//东、西、南有墙  
	  if(x==14)room[i-1][j][1]=room[i][j][0]=room[i][j+1][2]=room[i][j][3]=room[i+1][j][0]=room[i][j][1]=true;//北、东、南 
	  if(x==15)room[i-1][j][1]=room[i][j][0]=room[i+1][j][0]=room[i][j][1]=room[i][j-1][3]=room[i][j][2]=room[i][j+1][2]=room[i][j][3]=true;//东西南北都有墙
    }
}

void flood_fill(int x,int y){
	if(x<1 || y<1 || x>n || y>m || vis[x][y])return;
	vis[x][y]=id;
	cnt[id]++;
	if(!room[x][y][0])//北方没有墙
	  flood_fill(x-1,y);
	if(!room[x][y][1])//南方没有墙
	  flood_fill(x+1,y);
	if(!room[x][y][2])//西方没有墙
	  flood_fill(x,y-1);
	if(!room[x][y][3])//东方没有墙
	  flood_fill(x,y+1); 
}

void get_num_room(){
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++)
	  for(int j=1;j<=m;j++)
	   if(!vis[i][j]){
		  cnt[++id]=0;
		  flood_fill(i,j);
		  if(cnt[id]>MAX)MAX=cnt[id];
	   }
	printf("%d\n%d\n",id,MAX);
}

void delet_wall(){
	int i,j,cos=0;
	int wall_x,wall_y;
	char place;
	for(j=1;j<=m;++j)
	   for(i=n;i>=1;--i){
	  	if(room[i][j][0] && cos



你可能感兴趣的:(算法学习合集,===模拟===)