[关键字]:搜索
[题目大意]:给出一个显示原矩阵每一个2*2得小正方形的和的矩阵,每个元素都小于p,求出一个字典序最小的原矩阵a满足所给矩阵。
//================================================================================
[分析]:如果能求出第一行和第一列就可以推算出所有的格子。首先假设第一行和第一列都是0然后可以得到一个不满足小于p的矩阵c,然后进行调整。如果在(1,1)加了a1,1那只要再将所有非第一行第一列的行列和为奇数的格子加上a1,1行列和是偶数的减去a1,1结果不会改变,如果在(1,j)加a1,j只要把这一列偶数行减奇数行加就能保证和不变,如果在(i,1)加上ai,1只要这一行奇数列加偶数列减。这样可以得到a1,1的公式a[i][j]=c[i][j]+sign(i+j+1)*a[1][1]+sign(i-1)*a[1][j]+sign(j-1)*a[i][1],sign(x)=-1x.只搜索(1,1)和第一行,然后根据0<=a[i][j]<p判断a[i][1]的取值范围如果下界大于上界就剪枝。
[代码]:
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; const int MAXN=300; int a[MAXN][MAXN],c[MAXN][MAXN],s[MAXN][MAXN],l[MAXN][MAXN],r[MAXN][MAXN]; int n,m,p; int sign(int n) { return (n&1)?-1:1; } int Find(int i,int j) { return c[i][j]+sign(i+j+1)*a[1][1]+sign(i-1)*a[1][j]+sign(j-1)*a[i][1]; } bool DFS(int j) { if (j>m) return 1; for (a[1][j]=0;a[1][j]<p;++a[1][j]) { bool flag=1; for (int i=2;i<=n;++i) { int tl,tr; //tl=-(c[i][j]+sign(i+j+1)*a[1][1]+sign(i-1)*a[1][j])*sign(j-1); //tr=(p-1)-(c[i][j]+sign(i+j-1)*a[1][1]+sign(i-1)*a[1][j])*sign(j-1); tl=(c[i][j]+sign(i+j+1)*a[1][1]+sign(i-1)*a[1][j]-0)*(-1)*sign(j-1); tr=(c[i][j]+sign(i+j+1)*a[1][1]+sign(i-1)*a[1][j]-(p-1))*(-1)*sign(j-1); //printf("%d %d %d %d\n",tl,tr,i,j); if (tl>tr) swap(tl,tr); l[i][j]=max(l[i][j-1],tl); r[i][j]=min(r[i][j-1],tr); if (l[i][j]>r[i][j]) {flag=0;break;} } if (flag) if (DFS(j+1)) return 1; } return 0; } int main() { freopen("in.txt","r",stdin); freopen("out.txt","w",stdout); scanf("%d%d%d",&n,&m,&p); for (int i=1;i<=n;++i) for (int j=1;j<=m;++j) { scanf("%d",&s[i][j]); l[i][j]=0; r[i][j]=p-1; if (i!=1 && j!=1) c[i][j]=s[i][j]-c[i-1][j]-c[i][j-1]-c[i-1][j-1]; } for (a[1][1]=0;a[1][1]<p;++a[1][1]) if (DFS(2)) { for (int i=2;i<=n;++i) a[i][1]=l[i][m]; for (int i=1;i<=n;++i) { for (int j=1;j<=m;++j) printf("%d ",Find(i,j)); printf("\n"); } break; } fclose(stdin); fclose(stdout); return 0; }