题目
n*n(n<=500)的矩阵,第i行第j列的数a[i][j](-1e3<=a[i][j]<=1e3)
给出一个值d(1<=d<=1000),你可以执行若干次操作,每次你可以选一个位置,
并给这个位置的数+x(x可以为负),代价是x的绝对值
在满足总代价不超过n*n*d/2的情况下,
使得新矩阵相邻项的差的绝对值>=d,输出新矩阵
相邻:a[i][j]和a[i][j+1]认为相邻,a[i][j]和a[i+1][j]认为相邻
思路来源
jiangly B站讲解
题解
很容易验证题解是正确的,只是很难想到这个做法,想不到就没法做
先将矩阵黑白染色,
第一次,将黑色的数改成最近的d的奇数倍,将白色的数改成最近的d的偶数倍
第二次,将黑色的数改成最近的d的偶数倍,将白色的数改成最近的d的奇数倍
不难发现,由于每个数向左向右构成了一个长为d的区间,所以两次操作的代价和是d*n*n
所以较小代价的那次就是不超过n*n*d/2的,输出即可
代码
#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
typedef long long ll;
typedef double db;
typedef pair<int,int> P;
#define fi first
#define se second
#define pb push_back
#define dbg(x) cerr<<(#x)<<":"<<x<<" ";
#define dbg2(x) cerr<<(#x)<<":"<<x<<endl;
#define SZ(a) (int)(a.size())
#define sci(a) scanf("%d",&(a))
#define pt(a) printf("%d",a);
#define pte(a) printf("%d\n",a)
#define ptlle(a) printf("%lld\n",a)
#define debug(...) fprintf(stderr, __VA_ARGS__)
using namespace std;
const int N=505;
int n,d,a[N][N],b[N][N];
int main(){
sci(n),sci(d);
rep(i,1,n){
rep(j,1,n){
sci(a[i][j]);
}
}
rep(k,0,1){
int sum=0;
rep(i,1,n){
rep(j,1,n){
int x=(i+j+k)%2,v=a[i][j]/d*d,v2;
if(v<=a[i][j] && a[i][j]<=v+d)v2=v+d;
else v2=v-d;
b[i][j]=((v/d+x)%2==0?v:v2);
sum+=abs(b[i][j]-a[i][j]);
}
}
if(sum<=n*n*d/2){
rep(i,1,n){
rep(j,1,n){
printf("%d%c",b[i][j]," \n"[j==n]);
}
}
return 0;
}
}
assert(false);
return 0;
}