[dfs][思维] Luogu P3208 矩阵

题目描述

小Z近日闲来无事,便研究起矩阵来。他先谢了一个N\times MN×M的矩阵,每个格子里填入了一个小于P的非负整数,然后他对于每个2\times 22×2的子矩阵,算出了其中数的和。

譬如N=3N=3,M=3M=3,P=3P=3,小Z写的矩阵如下:

共有4个2\times 22×2的子矩阵,容易算出它们的和如下:

(第一行和第一列的0是为了格式美观而添加进去的)

现在小Z想试一试能不能根据这些和推算出原矩阵。由于小Z的数学并不好,因此这个任务就交给你了。

当然,小Z早就发现了,解很可能不唯一,譬如下面的矩阵算出的和与A相同:

示意图在有多个矩阵满足要求的情况下请你输出字典序最小的哪一个。

字典序的比较方式如下:对于两个解矩阵X和Y,找到X和Y不同的位置中行数最小的那一个格子,若有多个则取列数最小的那个格子,该位置较小的矩阵字典序较小。

譬如上述的矩阵A和B,第一个不同的格子应是第一行第二个格子,而A[1][2]<B[1][2],故矩阵A的字典序比B小。

另外,小Z的数学还没有差到加法都做错,因此保证输入数据都是有解的。

 

题解

  • 很容易就可以知道,对于求一个2*2的矩阵,如果矩阵内知道了三个数,那么这个矩阵就可以得出来了
  • 那么由此可以得出,如果我们确定了第一行和第一列的话,整个矩阵就可以得出来了,这样的话我们只用枚举399个就可以了
  • 因为要求字典序最小,我们先可以默认第一行第一列都为0,求出一个矩阵
  • 发现在(1,1)位置的数+k,那么在除了第一行和第一列的矩阵中,要合法,就要i+j为偶数的-=k,i+j为奇数的+=k即可
  • 同样在(1,j)位置+=k,那么只影响这一列,便偶数行号-=k,奇数行号+=k即可
  • 我们在保证了第一行最小的情况下,只要保证第一列最小即可满足字典序最小,因为确定第一行和第一列可以唯一的确定一个矩阵
  • 然后dfs上在加上一些剪枝就可以过了

代码

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <cstring>
 4 using namespace std;
 5 const int N=210;
 6 int n,m,P,l[N][N],r[N][N],a[N][N];
 7 int zf(int x) {return (x&1)?1:-1;}
 8 int calc(int x,int y) { return a[x][y]+zf(x+y)*a[1][1]+zf(y)*l[x][m]+zf(x)*a[1][y]; }
 9 bool dfs(int y)
10 {
11     if (y>m) return 1;
12     for(a[1][y]=0;a[1][y]<P;a[1][y]++)
13     {
14         bool flag=1;
15         for (int i=2;i<=n;i++)
16         {
17             int tl=(a[i][y]+a[1][1]*zf(i+y)+a[1][y]*zf(i))*zf(y+1),tr=(a[i][y]+a[1][1]*zf(i+y)+a[1][y]*zf(i)-(P-1))*zf(y+1);
18             if (tl>tr) swap(tl,tr);
19                l[i][y]=max(l[i][y-1],tl),r[i][y]=min(r[i][y-1],tr);
20                if (l[i][y]>r[i][y]) { flag=0; break; }
21         }
22        if (flag) if (dfs(y+1)) return 1;
23     }
24     return 0;
25  } 
26 int main()
27 {
28     scanf("%d%d%d",&n,&m,&P);
29     for (int i=1;i<=n;i++) for (int j=1;j<=m;j++)
30     {
31         scanf("%d",&a[i][j]);
32         if (i!=1&&j!=1) a[i][j]-=(a[i][j-1]+a[i-1][j]+a[i-1][j-1]);
33         r[i][j]=P-1;
34     }
35     for (;a[1][1]<P;a[1][1]++) if (dfs(2))
36     {
37         for (int i=1;i<=n;i++) for (int j=1;j<=m;j++) 
38             if (j==1&&i>1) printf("%d ",l[i][m]); else if (i==1&&j>1) printf("%d%c",a[1][j],j==m?'\n':' '); else printf("%d%c",calc(i,j),j==m?'\n':' ');
39         break;
40     }
41 }

 

posted @ 2019-07-25 18:22  BEYang_Z  阅读(208)  评论(0编辑  收藏  举报