题解 P5198 【[USACO19JAN]Icy Perimeter】
题目不难,只用BFS就可以AC。第一次我开小了数组RE了(开的1001*1001,我也不知道为什么RE),第二次就AC了。实现不难。
BFS适用于向多个方向同时扩展,像这种在矩阵中求联通块的问题用BFS思维和代码难度都不高。用DFS多半也可以,但是我懒而且弱,故只写BFS。这类题有一个模板:求细胞数量。本题相对仅多一个求周长的步骤,也可以用裸BFS解决,还是比较水的。
具体实现上我建了三个数组用于BFS,见代码。求面积详见P1451。求周长时遍历每个点,看它周围有几个空格,并将空格数加入答案。
#include<bits/stdc++.h>
int n,max,ans,ax,ay,d=0x7fffffff,book[5001][5001],s[5001][5001],s1[5001][5001],step[5][3]={{0,0,0},{0,0,1},{0,1,0},{0,-1,0},{0,0,-1}};
std::queue<std::pair<int,int> > b;//STL
inline int find(int x,int y)
{
int dis=0;
s1[x][y]=0;
b.push(std::make_pair(x,y));
while(!b.empty())
{
int x1=b.front().first,y1=b.front().second;
b.pop();
for(register int i=1;i<=4;i=-~i)
if(!s[x1+step[i][1]][y1+step[i][2]])
dis=-~dis;//在此统计周长
for(register int i=1;i<=4;i=-~i)
if(s1[x1+step[i][1]][y1+step[i][2]])
{
s1[x1+step[i][1]][y1+step[i][2]]=0;
b.push(std::make_pair(x1+step[i][1],y1+step[i][2]));
}//同求面积函数的BFS
}
return dis;
}
inline void Breadth_First_Search(int x,int y)
{
int res=0,f=find(x,y);//find求周长,这个函数主要负责求面积,顺便统计答案
book[x][y]=0;
b.push(std::make_pair(x,y));
while(!b.empty())
{
int x1=b.front().first,y1=b.front().second;
b.pop();
res=-~res;
for(register int i=1;i<=4;i=-~i)
{
if(book[x1+step[i][1]][y1+step[i][2]])
{
book[x1+step[i][1]][y1+step[i][2]]=0;
b.push(std::make_pair(x1+step[i][1],y1+step[i][2]));
}//step是扩展方向,这里向四个方向扩展并统计面积
}
}
if(res>ans||!(res^ans)&&f<d)
{
ans=res;
ax=x;
ay=y;
d=f;
}//按题目的要求,求出最优解。f是前面求出的周长。
return;
}
int main()
{
scanf("%d",&n);
for(register int i=1;i<=n;i=-~i)
for(register int j=1;j<=n;j=-~j)
{
char c;
scanf("%c",&c);
while(c=='\n')
scanf("%c",&c);
s[i][j]=book[i][j]=s1[i][j]=!(c^'#')?1:0;//赋值,3个数组
}
for(register int i=1;i<=n;i=-~i)
for(register int j=1;j<=n;j=-~j)
if(book[i][j])
Breadth_First_Search(i,j);//这里枚举每个点,对存在冰激凌的点进行BFS,并在BFS时删去本联通块
printf("%d %d",ans,d);
return 0;
}