题目描述:
一个
n∗m
n
∗
m
由0,1,2组成的矩阵,每次操作可以选取一个方格,使得ta加上2之后对3取模,周围的四个方格加上1后对3取模,在
n∗m
n
∗
m
操作次数内让整个矩阵变成0。输出一种方案。
分析:
模数好啊,原数=逆元
状压dp枚举每一行肯定不行了
然而想到了开灯问题,这道题可以用高斯消元!
设直接操作
(i,j)
(
i
,
j
)
的次数为
xi,j
x
i
,
j
ans(i,j)=2∗xi,j+xi−1,j+xi,j−1+xi+1,j+xi,j+1+a[i][j]=0 (mod 3)
a
n
s
(
i
,
j
)
=
2
∗
x
i
,
j
+
x
i
−
1
,
j
+
x
i
,
j
−
1
+
x
i
+
1
,
j
+
x
i
,
j
+
1
+
a
[
i
]
[
j
]
=
0
(
m
o
d
3
)
我们可以用gauss求解未知数
时间复杂度: O((nm)3) O ( ( n m ) 3 )
tip
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
const int p=3;
const int N=1003;
int a[N][N],n,m,tot,ans[N],cnt=0;
int get(int x,int y) {return (x-1)*m+y;}
int gauss(int n) {
int now=1,to;
for (int i=1;i<=n;i++) {
for (to=now;to<=n;to++)
if (abs(a[to][i])>0) break;
if (to>n) continue;
if (now!=to)
for (int j=1;j<=n+1;j++)
swap(a[now][j],a[to][j]);
for (int j=now+1;j<=n;j++)
if (a[j][i]) {
int t=((a[j][i]*a[now][i])%p+p)%p;
if (a[j][i]*a[now][i]<0) t=-t;
for (int k=1;k<=n+1;k++) {
a[j][k]-=t*a[now][k];
a[j][k]=(a[j][k]%p+p)%p;
}
}
now++;
}
for (int i=n;i>=1;i--) {
ans[i]=((a[i][n+1]*a[i][i])%p+p)%p;
cnt+=ans[i];
for (int j=i-1;j>=1;j--)
if (a[j][i]) {
a[j][n+1]-=a[j][i]*ans[i];
a[j][n+1]=(a[j][n+1]%p+p)%p;
}
}
}
int main()
{
int T;
scanf("%d",&T);
while (T--) {
memset(a,0,sizeof(a));
memset(ans,0,sizeof(ans));
cnt=0;
scanf("%d%d",&n,&m);
tot=n*m;
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++) {
int x; scanf("%d",&x);
int num=get(i,j);
a[num][tot+1]=(p-x%p)%p;
a[num][num]=2;
if (i>1) a[num][get(i-1,j)]=1;
if (j>1) a[num][get(i,j-1)]=1;
if (i<n) a[num][get(i+1,j)]=1;
if (j<m) a[num][get(i,j+1)]=1;
}
gauss(tot);
printf("%d\n",cnt);
for (int i=1;i<=n;i++)
for (int j=1;j<=m;j++) {
int num=get(i,j);
if (ans[num])
for (int k=1;k<=ans[num];k++)
printf("%d %d\n",i,j);
}
}
return 0;
}