备战省赛组队训练赛第五场(补题)问题 B: Bulbs (位运算^ 运算的应用)

题目描述
Greg has an m × n grid of Sweet Lightbulbs of Pure Coolness he would like to turn on. Initially, some of the bulbs are on and some are off. Greg can toggle some bulbs by shooting his laser at them. When he shoots his laser at a bulb, it toggles that bulb between on and off. But, it also toggles every bulb directly below it,and every bulb directly to the left of it. What is the smallest number of times that Greg needs to shoot his laser to turn all the bulbs on?

输入
The first line of input contains a single integer T (1 ≤ T ≤ 10), the number of test cases. Each test case starts with a line containing two space-separated integers m and n (1 ≤ m, n ≤ 400). The next m lines each consist of a string of length n of 1s and 0s. A 1 indicates a bulb which is on, and a 0 represents a bulb which is off.

输出
For each test case, output a single line containing the minimum number of times Greg has to shoot his laser to turn on all the bulbs.

样例输入
复制样例数据
2
3 4
0000
1110
1110
2 2
10
00
样例输出
1
2

提示
In the first test case, shooting a laser at the top right bulb turns on all the bulbs which are off, and does not
toggle any bulbs which are on.
In the second test case, shooting the top left and top right bulbs will do the job.
题意就是 一个n*m矩阵 0 代表该地方灯是关的,1代表灯是亮的,如果该表一个地方的状态,它的左侧所有灯和右侧所有灯都会改变 (0-1 1-0)
这个题听说直接暴力写不会超时,但还没亲自试一试,可以用位运算优化,用俩个标记数组分别标记行和列,如果初始化数组为0 的话,改变一次灯的状态就使它对1异或,这样如果改变俩次就会回到0,相当于没变(也可以直接++,然后对2取余),这样就需要从矩阵的右上角开始遍历,对每个元素如果他的标记行和列同为0或1 的话意味着状态不改变,进而再判断(代码注释)
下面是ac代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<stdio.h>
#include<algorithm>
#include<math.h>
#include<string.h>
using namespace std;
typedef long long ll;
const int maxn=3e7+7;
ll a[500][500];
ll vis1[500];
ll vis2[500];
int main()
{
    ll t;
    scanf("%lld",&t);
    while(t--)
    {
        memset(vis1,0,sizeof(vis1));
        memset(vis2,0,sizeof(vis2));
        ll num=0;
        ll n,m;
        scanf("%lld%lld",&n,&m);
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
                scanf("%1lld",&a[i][j]);								//scanf 格式化输入,题目中每次输入之间是没有空格的(%1d)
        }
        for(int i=1;i<=n;i++)
        {

            for(int j=m;j>=1;j--)
            {
                if(vis1[j]==1&&vis2[i]==1||vis1[j]==0&&vis2[i]==0)						//此时意味着状态未改变,若此时为0的话需要改变一下他的状态,并对该元素的左侧和下面标记
                {
                    if(a[i][j]==0)
                    {
                        //printf("%d %d\n",i,j);
                        num++;
                        vis1[j]^=1;
                        vis2[i]^=1;
                    }
                }
                else										//这是即为行列标记数组不同,意味着状态发生改变,若此时为1的话实则为 0 ,需要改变状态并标记
                {
                    if(a[i][j]==1)		
                    {
                        //printf("%d %d\n",i,j);
                        num++;
                        vis1[j]^=1;
                        vis2[i]^=1;
                    }
                }
            }
        }
        printf("%lld\n",num);
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值