洛谷 P7515 [省选联考 2021 A 卷] 矩阵游戏(构造,差分约束)
传送门
解题思路
其实这个题部分分还是很好想的,但是考场上硬是没拿满…………
直接说正解:
假如没有对元素大小的限制,很显然可以直接设定第n行和第m列的数字为0,一定能构造出一组解。
然后考虑限制。
我们发现,对某一行或者某一列的元素进行\(+1 \ -1\ +1 \ -1 \ +1\ -1\ \cdots\cdots\)对答案是没有影响的
所以对于每一个点(i,j),就需要保证一个不等式的成立:
\(0\leqslant b[i][j]\pm h[i]\pm l[j]\leqslant 10^6\)
便于差分约束系统,我们对于行这样:
\(\begin{vmatrix}
+1 & -1 & +1 & -1 \\
-1 & +1 & -1 & +1 \\
+1 & -1 & +1 & -1 \\
-1 & +1 & -1 & +1 \\
\end{vmatrix}\)
对于列这样:
\(\begin{vmatrix}
-1 & +1 & -1 & +1 \\
+1 & -1 & +1 & -1 \\
-1 & +1 & -1 & +1 \\
+1 & -1 & +1 & -1 \\
\end{vmatrix}\)
于是式子就分成了i+j为偶数和奇数两种情况。
最后连边跑一边差分约束即可。
AC代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<queue>
using namespace std;
const int maxn=605;
const int maxx=1000000;
int n,m,T,p[maxn],dis[maxn],cnt,vis[maxn],times[maxn],a[305][305],b[305][305];
struct node{
int v,next,w;
}e[305*305*2];
void insert(int u,int v,int w){
cnt++;
e[cnt].v=v;
e[cnt].w=w;
e[cnt].next=p[u];
p[u]=cnt;
}
queue<int> q;
bool spfa(){
while(!q.empty()) q.pop();
memset(dis,0x3f,sizeof(dis));
dis[0]=0;
q.push(0);
vis[0]=1;
while(!q.empty()){
int u=q.front();
q.pop();
vis[u]=0;
for(int i=p[u];i!=-1;i=e[i].next){
int v=e[i].v;
if(dis[v]>dis[u]+e[i].w){
times[u]++;
if(times[u]>n+m) return false;
dis[v]=dis[u]+e[i].w;
if(!vis[v]){
vis[v]=1;
q.push(v);
}
}
}
}
return true;
}
int main(){
scanf("%d",&T);
while(T--){
cnt=0;
memset(b,0,sizeof(b));
memset(times,0,sizeof(times));
memset(vis,0,sizeof(vis));
memset(p,-1,sizeof(p));
scanf("%d%d",&n,&m);
for(int i=1;i<=n+m;i++){
insert(0,i,0);
}
for(int i=1;i<n;i++){
for(int j=1;j<m;j++){
scanf("%d",&a[i][j]);
}
}
for(int i=n-1;i>=1;i--){
for(int j=m-1;j>=1;j--){
b[i][j]=a[i][j]-b[i+1][j]-b[i][j+1]-b[i+1][j+1];
}
}
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if((i+j)%2==0){
insert(i,j+n,b[i][j]);
insert(j+n,i,maxx-b[i][j]);
}else{
insert(j+n,i,b[i][j]);
insert(i,j+n,maxx-b[i][j]);
}
}
}
if(spfa()){
printf("YES\n");
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if((i+j)%2==0){
printf("%d ",b[i][j]+dis[i]-dis[n+j]);
}else{
printf("%d ",b[i][j]-dis[i]+dis[n+j]);
}
}
printf("\n");
}
}else{
printf("NO\n");
}
}
return 0;
}