[week14] Q老师与十字叉

题意

Q老师 得到一张 n 行 m 列的网格图,上面每一个格子要么是白色的要么是黑色的。

Q老师认为失去了 十字叉 的网格图莫得灵魂. 一个十字叉可以用一个数对 x 和 y 来表示, 其中 1 ≤ x ≤ n 并且 1 ≤ y ≤ m, 满足在第 x 行中的所有格子以及在第 y 列的 所有格子都是黑色的

例如下面这5个网格图里都包含十字叉
在这里插入图片描述

第四个图有四个十字叉,分别在 (1, 3), (1, 5), (3, 3) 和 (3, 5).

下面的图里没有十字叉
在这里插入图片描述
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 组数据的答案

输入样例

9
5 5
..*..
..*..
*****
..*..
..*..
3 4
****
.*..
.*..
4 3
***
*..
*..
*..
5 5
*****
*.*.*
*****
..*.*
..***
1 4
****
5 5
.....
..*..
.***.
..*..
.....
5 3
...
.*.
.*.
***
.*.
3 3
.*.
*.*
.*.
4 4
*.**
....
*.**
*.**

输出样例

0
0
0
0
0
4
1
1
2

提示


分析

  • 题目分析

这道题就是找到一个网格图和含十字叉网格图之间差的黑格子数。

十字叉的要求是任意一行全为黑格,任意一列也全为黑格。其中“任意”这两个限定词就大大降低了难度。所以只要找到含黑格数最多的一行和一列,再将它们所构成的十字叉结构中所差的黑格数输出即可。

问题在于,这样的做法存在一个误差。

假如有一个以上的列都是最大值,而记录的最大列与最大行的交叉处为黑色,而其他的最大列中存在至少一个列与最大行交叉处为空白,这就代表记录的最大列和最大行得出的答案并不是最小。因为交叉处为空白的最大列显然需要填补的格子会少一个。

这个其实也可以实现判断。在结束统计后,遍历记录的行数组和列数组,列出行和列中所有的最大值。再遍历这些最大值,找到一对交叉处为空格的行和列即可。若都没有交叉处则答案就是任意一对最大行和列对应的答案。

不过我后来改成了直接遍历行数组和列数组,将所有行和列对应的答案求出找最小。由于比较懒【orz】且这个修改思路不一定能写出来,所以就没有按着之前的直接锁定最大值的思路来完善代码了。


总结

  1. 又是被玄幻状态困扰的lazy孩子

代码

//
//  main.cpp
//  lab2
//
//

#include <iostream>
#include <string.h>
#include <vector>
#include <algorithm>
#include <limits.h>
using namespace std;

int row[50001];
int col[50001];
vector<int> black[50001];

int main()
{
    ios::sync_with_stdio(false);
    
    char c;
    int q = 0,n = 0,m = 0,max_row = 0,max_col = 0,ans = INT_MAX;
    cin>>q;
    
    while( q-- )
    {
        cin>>n>>m;
        
        ans = INT_MAX;
        max_col = 0;
        max_row = 0;
        memset(row, 0, sizeof(row));
        memset(col, 0, sizeof(col));
        for( int i = 0 ; i < n ; i++ )
            black[i].clear();
        
        for( int i = 0 ; i < n ; i++ )
        {
            for( int j = 0 ; j < m ; j++ )
            {
                cin>>c;
                if( c != '.' )
                {
                    row[i]++;           //记录每一行和每一列中的黑色格子数量
                    col[j]++;
                    
                    black[i].push_back(j);      //记录每一行里格子为黑色的列序号
                    
//                    if( col[j] > col[max_col] )
//                        max_col = j;
                }
            }
//            if( row[i] > row[max_row] )
//                max_row = i;
            
        }
        
        //直接在记录过程中记录黑色格子数最大的行和列
        //这种做法是正确的,但在计算机实现的过程中存在误差
        //也就是假如有一个以上的列都是最大值,而记录的最大列与最大行的交叉处为黑色,而其他的最大列中存在至少一个列与最大行交叉处为空白,这就代表记录的最大列和最大行得出的答案并不是最小。因为交叉处为空白的最大列显然需要填补的格子会少一个
        //这个其实也可以实现判断。但是在输入过程中判定不好实现,而结束之后循环和下面的做法其实差别不大。
        
//        ans = m - row[max_row] + n - col[max_col];
        
        int sum = 0;
        
        for( int i = 0 ; i < n ; i++ )      //遍历记录的行和列数组
        {
            for( int j = 0 ; j < m ; j++ )
            {
                sum = m + n - ( row[i] + col[j] );      //得到第i行和第j列需要填补的格子数量
                
                //如果交叉处为空白,则应该减少一个填补格子
                if( find(black[i].begin(), black[i].end(), j) == black[i].end() )
                    sum--;
                
                if( sum == 0 )      //如果为0,说明已找到一个十字叉,该网格图不需要再填补
                {
                    ans = 0;
                    break;
                }
                
                ans = min(ans, sum);        //取最小值
                
            }
        }
        
        cout<<ans<<endl;
        
    }
    
    return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

天翊藉君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值