B-Q老师与十字叉
一、题目描述
Q老师 得到一张 n 行 m 列的网格图,上面每一个格子要么是白色的要么是黑色的。
Q老师认为失去了 十字叉 的网格图莫得灵魂. 一个十字叉可以用一个数对 x 和 y 来表示,
其中 1 ≤ x ≤ n 并且 1 ≤ y ≤ m, 满足在第 x 行中的所有格子以及在第 y 列的 所有格子都是黑色的
Q老师 得到了一桶黑颜料,他想为这个网格图注入灵魂。
Q老师 每分钟可以选择一个白色的格子并且把它涂黑。现在他想知道要完成这个工作,最少需要几分钟?
Input
第一行包含一个整数 q (1 ≤ q ≤ 5 * 10^4) — 表示测试组数
对于每组数据:
第一行有两个整数 n 和 m (1 ≤ n, m ≤ 5 * 10^4, n * m ≤ 4 * 10^5) — 表示网格图的行数和列数
接下来的 n 行中每一行包含 m 个字符 — '.' 表示这个格子是白色的, '*' 表示这个格子是黑色的
保证 q 组数据中 n 的总和不超过 5 * 10^4, n*m 的总和不超过 4 * 10^5
Output
答案输出 q 行, 第 i 行包含一个整数 — 表示第 i 组数据的答案
Example
Input
9
5 5
..*..
..*..
*****
..*..
..*..
3 4
****
.*..
.*..
4 3
***
*..
*..
*..
5 5
*****
*.*.*
*****
..*.*
..***
1 4
****
5 5
.....
..*..
.***.
..*..
.....
5 3
...
.*.
.*.
***
.*.
3 3
.*.
*.*
.*.
4 4
*.**
....
*.**
*.**
Output
0
0
0
0
0
4
1
1
2
二、思路与算法
本题不需要用到高级的算法,只需要读懂题意,十字叉不需要整幅图中所有黑色格子都在十字叉中,只需要图中存在十字叉即可。
所以我们只需要找出最适合成为十字叉的交叉处的格子即可。
那么我们两次遍历棋盘,一次记录每一行的白格子数目,一次记录每一列的白格子数目。
之后第三次遍历整个棋盘,对每个格子都计算如果这个格子作为十字叉的交叉处,需要涂黑几个格子,记录最少的数目。
遍历完成后,这个最少数目就是答案。
三、代码实现
#include<iostream>
#include<string>
#include<algorithm>
#include<vector>
using namespace std;
int q=0; //测试组数
int n=0,m=0; //行列数
vector<string> board; //棋盘
int row[50010]={0};
int col[50010]={0};
int main(){
cin>>q;
for(int i=0;i<q;i++){
cin>>n>>m;
string tmp;
board.clear();
for(int j=0;j<n;j++){
tmp.clear(); cin>>tmp;
board.push_back(tmp);
} //输入结束
for(int j=0;j<n;j++){
row[j]=0;
for(int k=0;k<m;k++){
if(board[j][k]=='.'){row[j]++; }
}
} //计算每行白格子数目
for(int j=0;j<m;j++){
col[j]=0;
for(int k=0;k<n;k++){
if(board[k][j]=='.'){col[j]++; }
}
} //计算每列白格子数目
int ans=50010;
for(int j=0;j<n;j++){
for(int k=0;k<m;k++){
if(board[j][k]=='*'){
ans=min(ans,row[j]+col[k]);
}
else{
ans=min(ans,row[j]+col[k]-1);
}
}
} //寻找answer
cout<<ans<<"\n";
}
return 0;
}
四、经验与总结
- 理解题意很重要,一开始不理解什么图有十字叉,什么图没有十字叉。经过观察后发现,不需要整个图中所有黑色格子都属于十字叉,而是存在一个十字叉就可以。