P3208题解
题面
看完题目后,我们可以先发现:如果我们确定了原矩阵的第一行和第一列,那么结合给我们的
这很明显,因为题目定义对于每一个
那么,我们假设我们已经枚举出了矩阵
大家可以自己在纸上手推一下这个表格,大概就长这样。
经过整理,我们可以发现,
也不知道为什么我的同学管这玩意叫差分!
那么我们就可以先将
这样式子就处理了一半了,对于后面的与
证明如下:
对于
对于
对于
那么对于所有
同理可以证明另外两个部分,只是对于
那么,现在我们也证明了式子是正确的,就可以开始搜索了吗?
当然不够,我们还需要一些优化剪枝,而这个式子只是用来剪枝的。
我们一边枚举第一行的时候,我们就可以通过式子以及每一个元素的范围求出第一列每个元素
然后,我们就完美的完成了这一道简单的黑题。(话说我们的教练拿这玩意作为我们的 CSP-S 的复习题)
代码:
#include<bits/stdc++.h>
using namespace std;
int n,m,p,L[205],R[205],a[205][205],b[205][205],c[205][205];//a是给我们的,b是预处理出来的,c是答案,L、R用来存储范围
void dfs(int j){
if(j==m+1){//搜索完了
for(int i=2;i<=n;i++){
for(int j=2;j<=m;j++){
c[i][j]=a[i][j]-c[i-1][j]-c[i][j-1]-c[i-1][j-1];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
printf("%d ",c[i][j]);
}
puts("");
}//输出
exit(0);//结束
}
int l[205],r[205];
for(int i=2;i<=n;i++)l[i]=L[i],r[i]=R[i];//把范围先存下来
for(int k=0;k<p;k++){//枚举值
c[1][j]=k;//更新答案
bool flag=1;
if(j>1){
for(int i=2;i<=n&&flag;i++){
c[i][j]=b[i][j]+((i&1)?1:-1)*c[1][j]+(((j+i)&1)?1:-1)*c[1][1];
if(j&1){
if(c[i][j]>=p)flag=0;
else if(c[i][j]<0)L[i]=max(L[i],-c[i][j]);
else R[i]=min(R[i],p-c[i][j]-1);
}
else{
if(c[i][j]<0)flag=0;
else if(c[i][j]>=p)L[i]=max(L[i],c[i][j]+1-p);
else R[i]=min(R[i],c[i][j]);
}//更新范围
if(L[i]>R[i])flag=0;//不合法
c[i][1]=L[i];//合法,贪心的选择
}
}
if(flag)dfs(j+1);//是合法的,递归
for(int i=2;i<=n;i++)L[i]=l[i],R[i]=r[i];//否则回溯
}
}
signed main()
{
scanf("%d%d%d",&n,&m,&p);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)scanf("%d",&a[i][j]);
for(int i=2;i<=n;i++)L[i]=0,R[i]=p-1;//范围的处理
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)b[i][j]=a[i][j]-b[i-1][j]-b[i][j-1]-b[i-1][j-1];//预处理b
dfs(1);//搜索
return 0;
}