Yinchuan Regional 2019 B. So Easy

题目链接
Yinchuan Regional 2019
B. So Easy


B. So Easy

题意:

初始有个 n ∗ n n*n nn 的数组,每一个位置都是 0 0 0。每次可以选择一行或一列,给上面的位置加 1 1 1。进行若干次操作后选择一个位置改成 − 1 -1 1。问 − 1 -1 1 这个位置的数是什么。

思路:

操作有点像粘胶带。每次横着或竖着贴一张宽为 1 1 1,厚度为 1 1 1 的胶带,最后得到一个 n ∗ n n*n nn 的厚度数组。假设 ( x , y ) (x,y) (x,y) 坐标是 − 1 -1 1 的位置,现在变成了一张一张来撕,只留下第 x x x 行和第 y y y 列的胶带,然后两条上的胶带数量就是 − 1 -1 1 位置的数。

用两个数组 d c [ j ] dc[j] dc[j] d l [ i ] dl[i] dl[i] 分别描述第 j j j 列粘了几条胶带 和 第 i i i 行粘了几条胶带。显然如果每一列都粘了至少 x x x 个胶带的话,那么我们可以把它们看作是横着粘了 x x x 个胶带,反过来同理。所以我们不如一开始就把这些可变方向的胶带先移除掉,这样就一定会有一个位置 ( t x , t y ) (tx,ty) (tx,ty) 的厚度为 0 0 0。那么第 t x tx tx 行 和 第 t y ty ty 列一定没有贴胶带。

这时候其实 因为第 t x tx tx 行没有粘 横着的胶带,那么它的厚度就是竖着粘的胶带的条数而且它上面的每个值一定是这个值所在列上最小的(因为其他行上都多多少少粘了胶带,厚度一定会增加),具体来说,假如厚度数组为 a [ i ] [ j ] a[i][j] a[i][j],那么 a [ t x ] [ j ] = d c [ j ] ( 1 ≤ j ≤ n ) a[tx][j]=dc[j]\quad(1\le j\le n) a[tx][j]=dc[j](1jn),并且 d c [ j ] ≤ a [ i ] [ j ] ( 1 ≤ i ≤ n ) dc[j] \le a[i][j]\quad(1\le i\le n) dc[j]a[i][j](1in)。同理第 t y ty ty 列。因此第 t x tx tx 行就是我们上面定义的 d l [ i ] dl[i] dl[i] 数组,第 t y ty ty 列就是我们上面定义的 d c [ j ] dc[j] dc[j] 数组。

然后我们想,真的有必要一开始就把这些可变方向的胶带先移除掉吗,这相当于给所有位置减去 a [ i ] [ j ] a[i][j] a[i][j] 中最小的值,第 t x tx tx 行上的每个数都是它们所属列上最小的事实是没有变的。所以我们可以直接找到最小的那个数,记录 ( t x , t y ) (tx,ty) (tx,ty),先给每一行减去第 t x tx tx 行(注意别不要动第 t x tx tx 行),这时第 t y ty ty 列上的数也要减(这是因为 t x tx tx 行和 t y ty ty 列都包含上面说的可变方向的厚度,这一块是重复的,第一次减的时候给 t y ty ty 列也减一下,第二次的相减就不会重复减了),然后先给每一列减去第 t y ty ty 列(注意别不要动第 t y ty ty 列),这时第 t x tx tx 行上的数不要减(因为上面已经把重复部分减掉了,这里就不用重复减了)。这样就可以把除了第 t x tx tx 行和第 t y ty ty 列的所有位置都置为 0 0 0

现在把一个位置换成 − 1 -1 1 了,该怎么办?其实也好解决。我们把 − 1 -1 1 所在行列先隐藏不看,剩下的就可以用上面的思路求得 d l dl dl d c dc dc 数组,我们把胶带都撕掉, − 1 -1 1 所在行列上的也顺便撕掉,剩下的所有元素理应为 0 0 0 − 1 -1 1 所在位置也不例外,因此 − 1 -1 1 所在位置的减完之后的值与 − 1 -1 1 的差值就是 − 1 -1 1 这个位置上原本的值。

code:

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1005;
const int inf=2e9;

int n,a[maxn][maxn];
int x,y,tx,ty;

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])x=i,y=j;
		}
	int minn=inf;
	for(int i=1;i<=n;i++){
		if(i==x)continue;
		for(int j=1;j<=n;j++){
			if(j==y)continue;
			if(a[i][j]<minn){
				minn=a[i][j];
				tx=i;
				ty=j;
			}
		}
	}
	for(int i=1;i<=n;i++){
		if(i==tx)continue;
		for(int j=1;j<=n;j++)
			a[i][j]-=a[tx][j];
	}
	
	for(int j=1;j<=n;j++){
		if(j==ty)continue;
		for(int i=1;i<=n;i++){
			if(i==tx)continue;
			a[i][j]-=a[i][ty];
		}
	}
	
	cout<<-1-a[x][y];
	
	return 0;
} 
  • 24
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值