Codeforces Round #684 (Div. 2) C1. Binary Table (Easy Version)(构造算法)

博客围绕一个算法问题展开,给定 n * m 矩阵,可选择 2 * 2 子矩阵中的三个元素取反,目标是将矩阵元素全变为 0 且操作不超 3 * n * m 次。介绍了问题题意,分析了 2 * 2 子矩阵操作方法,还给出代码思路。

题目链接:https://codeforc.es/contest/1440/problem/C1

This is the easy version of the problem. The difference between the versions is in the number of possible operations that can be made. You can make hacks if and only if you solved both versions of the problem.

You are given a binary table of size n×m. This table consists of symbols 0 and 1.

You can make such operation: select 3 different cells that belong to one 2×2 square and change the symbols in these cells (change 0 to 1 and 1 to 0).

Your task is to make all symbols in the table equal to 0. You are allowed to make at most 3nm operations. You don’t need to minimize the number of operations.

It can be proved that it is always possible.

Input
The first line contains a single integer t (1≤t≤5000) — the number of test cases. The next lines contain descriptions of test cases.

The first line of the description of each test case contains two integers n, m (2≤n,m≤100).

Each of the next n lines contains a binary string of length m, describing the symbols of the next row of the table.

It is guaranteed that the sum of nm for all test cases does not exceed 20000.

Output
For each test case print the integer k (0≤k≤3nm) — the number of operations.

In the each of the next k lines print 6 integers x1,y1,x2,y2,x3,y3 (1≤x1,x2,x3≤n,1≤y1,y2,y3≤m) describing the next operation. This operation will be made with three cells (x1,y1), (x2,y2), (x3,y3). These three cells should be different. These three cells should belong into some 2×2 square.

Example

input

5
2 2
10
11
3 3
011
101
110
4 4
1111
0110
0110
1111
5 5
01011
11001
00010
11011
10000
2 3
011
101

output

1
1 1 2 1 2 2
2 
2 1 3 1 3 2
1 2 1 3 2 3
4
1 1 1 2 2 2 
1 3 1 4 2 3
3 2 4 1 4 2
3 3 4 3 4 4
4
1 2 2 1 2 2 
1 4 1 5 2 5 
4 1 4 2 5 1
4 4 4 5 3 4
2
1 3 2 2 2 3
1 2 2 1 2 2

题意

给出一个 n * m 矩阵,每次可以选择一个 2 * 2 的子矩阵,选择子矩阵中的三个元素将其取反,输出将 n * m 全部都变为 0 的步骤(不能超过 3 * n * m 次)。

分析

对于一个 2 * 2 的子矩阵,我们可以用如下方法使得最后只改变其中一个点,其他位置的点同理,最多用 3 * n * m 步。
在这里插入图片描述

代码
#include<bits/stdc++.h>
using namespace std;

int t;
int n,m;
int ans[30007][6],cnt;
char g[107][107];

void add(int x1,int y1,int x2,int y2,int x3,int y3)
{
	ans[++cnt][0] = x1;
	ans[cnt][1] = y1;
	ans[cnt][2] = x2;
	ans[cnt][3] = y2;
	ans[cnt][4] = x3;
	ans[cnt][5] = y3;
}

int main()
{
	scanf("%d",&t);
	while(t--)
	{
		cnt = 0;
		scanf("%d%d",&n,&m);getchar();
		for(int i=1;i<=n;i++) scanf("%s",g[i] + 1);
		for(int i=1;i<n;i++)
			for(int j=1;j<m;j++)
				if(g[i][j] == '1')
				{
					add(i, j, i, j + 1, i + 1, j + 1);
					add(i, j, i, j + 1, i + 1, j);
					add(i, j, i + 1, j, i + 1, j + 1);
				}
		for(int j=1;j<m;j++)
			if(g[n][j] == '1')
			{
				int i = n;
				add(i, j, i - 1, j, i, j + 1);
				add(i, j, i, j + 1, i - 1, j + 1);
				add(i, j, i - 1, j, i - 1, j + 1);
			}
		for(int i=1;i<n;i++)
			if(g[i][m] == '1')
			{
				int j = m;
				add(i, j, i, j - 1, i + 1, j);
				add(i, j, i, j - 1, i + 1, j - 1);
				add(i, j, i + 1, j, i + 1, j - 1);
			}
		if(g[n][m] == '1')
		{
			int i = n, j = m;
			add(i, j, i, j - 1, i - 1, j - 1);
			add(i, j, i, j - 1, i - 1, j);
			add(i, j, i - 1, j, i - 1, j - 1);
		}
		printf("%d\n",cnt);
		for(int i=1;i<=cnt;i++)
		{
			for(int j=0;j<6;j++)
				printf("%d ",ans[i][j]);
			printf("\n");
		}
	}
	return 0;
}
Codeforces Round 1000 Div. 2 F1 题目 "Counting Is Not Fun (Easy Version)" 中,问题的核心是计算满足特定条件的子数组数量,而这些子数组的元素值与它们在子数组中的位置相关。 ### 题目简述 题目要求找到数组中满足特定条件的子数组数量。具体而言,对于一个长度为 $ n $ 的数组 $ a $,要求统计所有子数组 $ [l, r] $(其中 $ 1 \leq l \leq r \leq n $)的数量,使得对于该子数组内的每个位置 $ i $(相对于子数组的起始点 $ l $ 的偏移),满足 $ a[i] = i - l $。 ### 解题思路 1. **滑动窗口方法**:可以使用滑动窗口的思想来解决这个问题。我们遍历数组,尝试找到尽可能长的连续段,其中每个元素的值等于其相对于当前段起始位置的偏移量。对于每一个起始位置 $ l $,找到最大的 $ r $,使得所有 $ a[i] = i - l $ 对于 $ i \in [l, r] $ 成立。 2. **统计符合条件的子数组数量**:对于每一个满足条件的连续段,长度为 $ len $,则该段内可以形成的子数组数目为 $ len \times (len + 1) / 2 $。这是因为长度为 $ len $ 的连续段可以形成 $ len + (len - 1) + ... + 1 = len \times (len + 1) / 2 $ 个子数组。 3. **遍历数组**:通过遍历数组并维护当前段的起始位置,可以高效地找到所有符合条件的连续段并统计其对应的子数组数量。 ### 时间复杂度 该算法的时间复杂度为 $ O(n) $,因为每个元素最多被访问一次,且没有嵌套循环。 ### 示例代码 以下是一个实现该思路的 Python 示例代码: ```python n = int(input()) a = list(map(int, input().split())) count = 0 i = 0 while i < n: if a[i] != 0: i += 1 continue # 找到最长的连续段 j = i while j < n and a[j] == j - i: j += 1 length = j - i count += length * (length + 1) // 2 i = j print(count) ``` ### 代码说明 1. 首先读取输入的数组。 2. 遍历数组,寻找所有起始位置 $ i $,其中 $ a[i] = 0 $,因为这是可能的子数组起点。 3. 对于每一个起点 $ i $,找到最长的连续段 $ [i, j) $,使得所有元素满足 $ a[k] = k - i $。 4. 根据连续段的长度计算符合条件的子数组数量,并累加到最终结果中。 ### 相关优化 在实现中,需要注意避免重复计算或遗漏某些情况,例如当数组中存在多个连续段时,确保每个段都被正确分割并计算。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值