来源:JZOJ
题目描述
要开始他的冰激凌生意了!他制造了一台可以生产冰激凌球的机器,然而不幸的是形状不太规则,所以他现在希望优化一下这台机器,使其产出的冰激凌球的形状更加合理。 机器生产出的冰激凌的形状可以用一个 的矩形图案表示,例如:
##....
....#.
.#..#.
.#####
...###
....##
每个 ‘.’ 字符表示空的区域,每个 ‘#’ 字符表示一块 的正方形格子大小的冰激凌。
不幸的是,机器当前工作得并不是很正常,可能会生产出多个互不相连的冰激凌球(上图中有两个)。一个冰激凌球是连通的,如果其中每个冰激凌的正方形格子都可以从这个冰激凌球中其他所有的冰激凌格子出发重复地前往东、南、西、北四个方向上相邻的冰激凌格子所到达。
想要求出他的面积最大的冰激凌球的面积和周长。冰激凌球的面积就是这个冰激凌球中 ‘#’ 的数量。如果有多个冰激凌球并列面积最大,他想要知道其中周长最小的冰激凌球的周长。在上图中,小的冰激凌球的面积为 ,周长为 ,大的冰激凌球的面积为 ,周长为 。
注意一个冰激凌球可能在中间有“洞”(由冰激凌包围着的空的区域)。如果这样,洞的边界同样计入冰激凌球的周长。冰激凌球也可能出现在被其他冰激凌球包围的区域内,在这种情况下它们计为不同的冰激凌球。例如,以下这种情况包括一个面积为1的冰激凌球,被包围在一个面积为 的冰激凌球内:
#####
#...#
#.#.#
#...#
#####
同时求得冰激凌球的面积和周长十分重要,因为 最终想要最小化周长与面积的比值,他称这是他的冰激凌的“冰周率”。当这个比率较小的时候,冰激凌化得比较慢,因为此时冰激凌单位质量的表面积较小。
解题思路
- 显然,冰激凌球的最大面积就是求最大连通块,那么,面积怎么求呢?
- 不妨设想一下,如果我们找到一个点不是冰激凌或在地图之外,说明这个点是冰激凌球的边缘,那不就增加了一个单位的周长吗?
- 所以,这道题就迎刃而解了,一个裸的连通块板子
美妙的Code
#include <bits/stdc++.h>
using namespace std;
int fx[4]={0,0,1,-1};
int fy[4]={1,-1,0,0};
int n,area,girth,S=0,C=0;
int f[1005][1005];
char a[1005][1005];
void dfs(int x,int y)
{
if (f[x][y]) return;
f[x][y]=1; //标记为走过
area++; //面积增加1
for (int i=0;i<4;i++) //四个方向
{
int xx=x+fx[i],yy=y+fy[i];
if (xx<1 || xx>n || yy<1 || yy>n || a[xx][yy]=='.') girth++; //周长加1
if (a[xx][yy]=='#') dfs(xx,yy); //如是冰激凌格继续dfs
}
}
void work()
{
memset(f,0,sizeof(f));
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
if (a[i][j]=='#' && !f[i][j]) //以每个冰激凌格为起点,求连通块
{
area=0,girth=0;
dfs(i,j);
if (area>S) //更新面积
{
S=area;
C=girth;
}
else
if (area==S && girth<C) //更新周长
{
C=girth;
}
}
}
}
int main()
{
freopen("perimeter.in","r",stdin);
freopen("perimeter.out","w",stdout);
scanf("%d",&n);
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
cin >> a[i][j];
}
work();
printf("%d %d",S,C);
return 0;
}