题意
给你一个二维矩阵,每次可以对一行或者一列加1(modk),问,最少需要多少次操作,能让整个矩阵变成全0。
思路
首先,我们先考虑对每行/列加这个操作,很容易想到,用
r
i
r_i
ri?表示给第i行加的次数,
c
i
c_i
ci?表示给第i列加了多少次。那么,我们需要求一个约束条件为
r
i
+
c
i
+
a
i
j
=
0
(
m
o
d
k
)
r_i+c_i+a_{ij}=0(modk)
ri?+ci?+aij?=0(modk),
∑
(
r
i
+
c
i
)
\sum(r_i+c_i)
∑(ri?+ci?)的最小正整数解。
先考虑什么情况下有解,稍微观察下可以发现:
r
i
和
c
i
r_i和c_i
ri?和ci?总共有n+m个未知数,而方程有
n
?
m
n*m
n?m个
这就提示我们,里面有很多方程都是线性相关的,也就是说,我们可以消除掉一些方程!
现在尝试一下消除,我们不妨假设
r
1
r_1
r1?是已知的,那么
c
j
=
?
r
1
?
a
1
j
(
m
o
d
k
)
c_j=-r_1-a_{1j}(modk)
cj?=?r1??a1j?(modk)。这样我们便求出了所有的
c
j
c_j
cj?。可以得出
c
1
=
?
r
1
?
a
11
(
m
o
d
k
)
c_1=-r_1-a_{11}(modk)
c1?=?r1??a11?(modk)
因为c1已经求出来了,我们可以求出所有的
r
i
r_i
ri?,与上面类似
r
i
=
?
c
1
?
a
i
1
(
m
o
d
k
)
=
r
1
+
a
11
?
a
i
1
r_i=-c_1-a_{i1} (modk)=r_1+a_{11}-a_{i1}
ri?=?c1??ai1?(modk)=r1?+a11??ai1?。
把
r
i
r_i
ri?和
c
i
c_i
ci?代入
r
i
+
c
i
+
a
i
j
=
0
(
m
o
d
k
)
r_i+c_i+a_{ij}=0(modk)
ri?+ci?+aij?=0(modk),可以发现
r
1
r_1
r1?可以消掉!得到
a
11
?
a
i
1
?
a
1
j
+
a
i
j
=
0
(
m
o
d
k
)
a_{11}-a_{i1}-a_{1j}+a_{ij}=0(modk)
a11??ai1??a1j?+aij?=0(modk),所以只要对于所有的数字检验一下这个等式的成立性,就可以求出是否有解了。
那么继续考虑第二个问题,怎么求一个最优解
∑
(
r
i
+
c
i
)
\sum(r_i+c_i)
∑(ri?+ci?)?
先来个结论,
r
i
r_i
ri?或者
c
i
c_i
ci?里面必定有一个为0。
证明:假设最优解中,
r
i
r_i
ri?和
c
i
c_i
ci?全都不为0。
-
n
>
=
m
n >= m
n>=m, 可以对所有的
r
i
?
1
,
c
i
+
1
r_i-1,c_i+1
ri??1,ci?+1,那么整个矩阵的
a
i
j
a_{ij}
aij?都不变,而且操作次数减少了
n
?
m
>
=
0
n-m>=0
n?m>=0次。会使得结果不会变差,甚至变得更好
-
n
<
m
n < m
n<m, 可以对所有的
r
i
+
1
,
c
i
?
1
r_i+1,c_i-1
ri?+1,ci??1,那么整个矩阵的
a
i
j
a_{ij}
aij?都不变,而且操作次数减少了
m
?
n
>
0
m-n>0
m?n>0次。会使得结果变好。
我们可以做上面两种操作,直到
c
i
,
r
i
c_i, r_i
ci?,ri?有一个为0,并且解更优。矛盾!所以结论得证。
那么有一个为0有什么用呢?
我们不妨假设有一个
r
i
r_i
ri?为0,根据等式
r
i
+
c
i
+
a
i
j
=
0
(
m
o
d
k
)
r_i+c_i+a_{ij}=0(modk)
ri?+ci?+aij?=0(modk),我们可以求出所有的
c
i
c_i
ci?,再代入一次进而再求出所有的
r
i
r_i
ri?,这样就可以求出一个解了。
列的做法同理,我们的做法就是,暴力枚举哪一行/列的操作次数为0即可。
#include<bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10, M = 2 * N;
typedef long long LL;
typedef pair<int,int> PII;
using tp = tuple<int,int,int>;
bool multi = false;
int n, m, k;
int a[N][N];
inline int read()
{
int x=0,f=1;char c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
return f*x;
}
int mod(int x, int y = k) {
return (x % y + y) % y;
}
bool check() {
for(int i = 1; i <= n; i++) {
for(int j = 1; j <= m; j++)
if(mod(a[1][1] - a[1][j] - a[i][1] + a[i][j]) != 0) return false;
}
return true;
}
LL cal1(int x) {
int c1 = mod(-a[x][1]);
LL res = 0;
for(int i = 1; i <= n; i++) res += mod(-a[i][1] - c1);
for(int i = 1; i <= m; i++) res += mod(-a[x][i]);
return res;
}
LL cal2(int x) {
int r1 = mod(-a[1][x]);
LL res = 0;
for(int i = 1; i <= n; i++) res += mod(-a[i][x]);
for(int i = 1; i <= m; i++) res += mod(-r1 - a[1][i]);
return res;
}
void solve() {
n = read(), m = read(), k = read();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++) a[i][j] = read();
if(!check()) {
puts("-1");
return;
}
LL res = 1e18;
for(int i = 1; i <= n; i++) res = min(res, cal1(i));
for(int i = 1; i <= m; i++) res = min(res, cal2(i));
cout << res << endl;
}
int main()
{
#ifdef ONLINE_JUDGE
#else
freopen("I.txt", "r", stdin);
#endif
int T = 1;
if(multi) cin >> T;
while(T--) solve();
return 0;
}
|