[HNOI2010] 矩阵 matrix

标签:dfs+剪枝。

题解:

  这道题看着就像一道dfs题目,没有什么算法可以用来算这个东西,于是想想暴搜。
  如果我们确定因为是2*2的子矩阵的和,如果确定了其中三个,那么就可以确定第四个,发现如果确定了第一行和第一列的话,就可以确定整个矩阵了,于是我们枚举只有399个了。
  因为要求字典序最小,我们先默认第一行和第一列全部是0,求出一个矩阵。我们先搜索第一行,从左到右。发现在(1,1)位置的数+k,那么在除了第一行和第一列的矩阵中,要合法,就要i+j为偶数的-=k,i+j为奇数的+=k即可。同样在(1,j)位置+=k,那么只影响这一列,便偶数行号-=k,奇数行号+=k即可。我们在保证了第一行最小的情况下,只要保证第一列最小即可满足字典序最小,因为确定第一行和第一列可以唯一的确定一个矩阵。
  第一行我们暴搜,当然需要剪枝了,对于每搜到第一行的一个数,就要扫一遍这一列,并且更新这一列的每个元素的所在行的行首的范围,也就是矩阵第一列每一个元素的范围,因为矩阵中的每一个元素只被三个数影响,那就是(1,1),以及行首与列首。更新了第一列每一个元素的范围之后,如果冲突即(L>R)那么就返回0。
  这里有一个技巧:按道理范围只要开一个O(n)的数组即可,但是这样需要备份,因为如过搜索失败了,那么回溯也要恢复范围数组的值,很麻烦,那么我们直接开一个二维数组,每次更新时取上一列的值与当前值比较之后再更新,这样就没有回溯的问题了,因为我们是从左到右枚举第一行的。

 1 #include<cstdio>
 2 #include<iostream>
 3 #include<algorithm>
 4 using namespace std;
 5 const int MAXN=205,INF=0x3f3f3f3f;
 6 int n,m,P;
 7 int L[MAXN][MAXN],R[MAXN][MAXN],A[MAXN][MAXN];
 8 inline int gi(){int res; scanf("%d",&res); return res;}
 9 int F(int x){return (x&1)?1:-1;}
10 int cal(int x,int y) { return A[x][y]+F(x+y)*A[1][1]+F(y)*L[x][m]+F(x)*A[1][y]; }
11 bool dfs(int y)
12 {
13   if(y>m)return 1;
14   int bacl[MAXN]={0},bacr[MAXN]={0};
15   for(A[1][y]=0;A[1][y]<P;A[1][y]++)
16     {
17       bool flag=1;
18       for(int i=2;i<=n;i++)
19         {
20           int tl=(A[i][y] + A[1][1]*F(i+y) + A[1][y]*F(i)) * F(y+1);
21           int tr=(A[i][y] + A[1][1]*F(i+y) + A[1][y]*F(i)-(P-1)) * F(y+1);
22           if(tl>tr)swap(tl,tr);
23           L[i][y]=max(L[i][y-1],tl);
24           R[i][y]=min(R[i][y-1],tr);
25           if(L[i][y]>R[i][y])
26             {flag=0; break;}
27         }
28       if(flag)
29         if(dfs(y+1))
30           return 1;
31     }
32   return 0;
33 }
34 int main()
35 {
36   n=gi(); m=gi(); P=gi();
37   for(int i=1;i<=n;i++)
38     {
39       for(int j=1;j<=m;j++)
40         {
41           A[i][j]=gi();
42           if(i!=1 && j!=1)
43             A[i][j]-=(A[i][j-1]+A[i-1][j]+A[i-1][j-1]);
44           R[i][j]=P-1;
45         }
46     }
47   for(;A[1][1]<P;A[1][1]++)
48     {
49       if(dfs(2))
50         {
51           for(int i=1;i<=n;i++)
52             for(int j=1;j<=m;j++)
53               {
54                 if(j==1 && i>1)
55                   printf("%d ",L[i][m]);
56                 else if(i==1 && j>1)
57                   printf("%d%c",A[1][j],j==m?'\n':' ');
58                 else
59                   printf("%d%c",cal(i,j),j==m?'\n':' ');
60               }
61           return 0;
62         }
63     }
64   return 0;
65 }

转载于:https://www.cnblogs.com/D-O-Time/p/7999880.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值