洛谷 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;
}
posted @ 2021-05-19 23:42  尹昱钦  阅读(70)  评论(0编辑  收藏  举报