本期主要讲解与上期相同内容(雾。
例题
T1
在整个矩阵外加一圈 \(0\),使得包围圈外的 \(0\) 形成一整个连通块。求出这个连通块并标记为 \(1\),然后输出即可。
#include<bits/stdc++.h>
using namespace std;
int n;
int dx[]={-1,0,1,0},dy[]={0,1,0,-1};
int a[31][31],g[31][31];
void dfs(int x,int y){
if(x<0||x>n+1||y<0||y>n+1||g[x][y]!=0) return;
g[x][y]=1;
for(int i=0;i<4;i++) dfs(x+dx[i],y+dy[i]);
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
cin>>a[i][j];
if(!a[i][j]) g[i][j]=0;
else g[i][j]=1;
}
}
dfs(0,0);
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(!g[i][j]) cout<<"2 ";
else cout<<a[i][j]<<' ';
}
cout<<'\n';
}
return 0;
}
T2
求连通块很简单,本题的难点在于所有连通块必须为矩形。
如图,一个不是矩形的连通块可以看作是几个相邻的矩形拼凑在一起组成的。
而两个相邻矩形之间会形成一些的“拐角”,即在一个 \(2 \times 2\) 的正方形中的三个角是 #
,而一个角是 .
。
由此,我们便可枚举整个矩阵中所有 \(2 \times 2\) 的矩形,若产生了“拐角”,则直接输出 Bad placement.
即可。
我的代码并未全面判断,但是却过了 \(qwq\),说明数据太水了。
#include<bits/stdc++.h>
using namespace std;
int r,c,ans;
int dx[]={-1,0,1,0},dy[]={0,1,0,-1};
bool vis[1031][1031];
char a[1031][1031];
void dfs(int x,int y){
vis[x][y]=1;
for(int i=0;i<4;i++){
int xx=x+dx[i],yy=y+dy[i];
if(xx>=1&&xx<=r&&yy>=1&&yy<=c&&a[xx][yy]=='#'&&!vis[xx][yy])
dfs(xx,yy);
}
}
int main(){
cin>>r>>c;
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++)
cin>>a[i][j];
bool f=1;
for(int i=1;i+1<=r;i++)
for(int j=1;j+1<=c;j++){
if(a[i][j]=='#'&&a[i+1][j]=='#'&&a[i][j+1]=='#'&&a[i+1][j+1]!='#'){
f=0; break;
}
if(a[i][j]!='#'&&a[i+1][j]=='#'&&a[i][j+1]=='#'&&a[i+1][j+1]=='#'){
f=0; break;
}
}
if(!f){ cout<<"Bad placement."; return 0; }
for(int i=1;i<=r;i++)
for(int j=1;j<=c;j++)
if(a[i][j]=='#'&&!vis[i][j])
ans++,dfs(i,j);
cout<<"There are "<<ans<<" ships.";
return 0;
}
T3
只有 shaber 才会去想到对于每次询问进行 \(\text{DFS}\) 求出连通块大小(没错说的就是我)。
考虑优化搜索。我们想到,因为连通块始终是不变的,所以我们可以进行预处理。
具体的,我们对每个连通块存入 \(ans\) 数组并依次对其编号,并在搜索连通块的过程中顺便对于每个点 \((x,y)\) 求出包含它的连通块编号 \(id\),存入 \(pos_{x,y}\) 中。
这样,我们便实现了 \(O(1)\) 回答每次询问,总的时间复杂度为 \(O(n^2+m)\)。
最后,需要注意的是:
-
连通块个数 \(num\) 每次预处理前必须初始化为 \(1\);
-
因为读入的矩阵无空格,所以需要使用 \(char\) 类型保存矩阵。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,id,num;
int ans[1000031],pos[1031][1031];
int dx[]={-1,0,1,0},dy[]={0,1,0,-1};
bool vis[1031][1031];
char a[1031][1031];
void dfs(int x,int y){
vis[x][y]=1,pos[x][y]=id;
for(int i=0;i<4;i++){
int xx=x+dx[i],yy=y+dy[i];
if(xx>=1&&xx<=n&&yy>=1&&yy<=n&&!vis[xx][yy]&&a[xx][yy]!=a[x][y])
num++,dfs(xx,yy);
}
}
signed main(){
cin>>n>>m;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>a[i][j];
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++){
if(!vis[i][j]){
id++,num=1;
dfs(i,j);
ans[id]=num;
}
}
while(m--){
int x,y; cin>>x>>y; cout<<ans[pos[x][y]]<<'\n';
}
return 0;
}
习题
T4
求周长参考上期 T5。
注意点就是需要向八方向搜索,并且只有当扩展方向为上 / 下 / 左 / 右且即将扩展的点为 .
或越界了,才能将周长累加。
#include<bits/stdc++.h>
using namespace std;
int n;
char ice[1031][1031];
int S,C,s,c;
bool vis[1031][1031];
int dx[]={-1,0,1,0},dy[]={0,1,0,-1};
void update(){
if(s>S) S=s,C=c;
if(s==S&&c<C) C=c;
}
void dfs(int x,int y){
if(vis[x][y]) return;
vis[x][y]=1,s++;
for(int i=0;i<4;i++){
int xx=x+dx[i],yy=y+dy[i];
if(xx<1||xx>n||yy<1||yy>n||ice[xx][yy]=='.') c++;
if(ice[xx][yy]=='#') dfs(xx,yy);
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)
for(int j=1;j<=n;j++)
cin>>ice[i][j];
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(ice[i][j]=='#'&&!vis[i][j]){
s=c=0;
dfs(i,j);
update();
}
}
}
cout<<S<<' '<<C;
return 0;
}