P7515 矩阵游戏
P7515 矩阵游戏 题解
题意简述:给定\((n-1)\times(m-1)\)的矩阵\(b\),求是否存在一个\(n\times m\)的矩阵满足\(b_{i,j}=a_{i,j}+a_{i+1,j}+a_{i,j+1}+a_{i+1,j+1}\)且\(\forall a_{i,j}\)为小于等于\(10^6\)次方的非负整数。
数据范围:\(n,m\le 300,0\le a_{i,j}\le 10^6\)
Solution
发现很像线性规划问题。\(a_{i,j}\)有两个限制,\(b_{i,j}=a_{i,j}+a_{i+1,j}+a_{i,j+1}+a_{i+1,j+1}\)和\(0\le a_{i,j}\le 10^6\)。
观察性质,考虑差分约束。用条件1将条件2构造成\(a-b\le c\)或\(a-b\ge c\)的形式。
显然构造三角不等式要让原矩阵支持加/减一个数,观察矩阵\(a\)的性质。
- 性质1:对于矩阵\(a\)的每一行/每一列,让第一个元素\(+x\),第二个元素\(-x\),第三个元素\(+x\),\(b\)矩阵不会改变。
对于每个j经过的\(b_{i,j}\),有且仅有 2 个元素被经过,一加一减正好抵消。
设第\(i\)行加上了\(x_i\),第\(i\)列加上了\(y_i\)。
所以,画出\(a\)矩阵的累加矩阵\(c\):
所以\(0\le a_{i,j}+c_{i,j}\le10^6\),的\(-c_{i,j}\le a_{i,j}\le 10^6-c_{i,j}\)。
我们得到了我们需要的不等式,但是由于一个问题,累加矩阵\(c\)不仅有\(a-b\)的形式,还有\(a+b\)的形式,前者是我们熟悉的差分约束,但后者我们还不会处理。
- 性质2:对矩阵\(a\)进行黑白染色,记一个黑格为\(x-y\),白格为\(y-x\),发现对于每一行/每一列的颜色总为黑白交替的,由于黑白格的符号不相同,可直接染色。
本质上就是选出一些行和列,将\(x\)或\(y\)的符号取反。
一起手玩一下:
先观察原矩阵中\(x\)的符号:
第一、三行不变,改变第二、四行。
只观察\(x\)的符号:
同理,\(y\)也能染成这种正负交错的形式,一正一负正好是我们要的。
所以,操作完的矩阵为:
妙哉妙哉。然后差分约束即可。
代码如下:
#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int N=1e6+10,M=2e6+10;
int head[N],nxt[M],edge[M],ver[M],tot;
void add(int x,int y,int z){
ver[++tot]=y,edge[tot]=z,nxt[tot]=head[x],head[x]=tot;
}
ll d[610];
int a[310][310],b[310][310],n,m,cnt[610];
bool v[N];
deque<int>q;//spfa优化
bool spfa(){
memset(v,0,sizeof(v)),memset(cnt,0,sizeof(cnt)),memset(d,0x3f,sizeof(d));
d[1]=0,q.push_back(1);//v[1]=1;
while(q.size()){
int x=q.front();q.pop_front();
++cnt[x],v[x]=0;
if(cnt[x]>n+m)return 0;
for(int i=head[x];i;i=nxt[i]){
int y=ver[i];
if(d[y]>d[x]+edge[i]){
d[y]=d[x]+edge[i];
if(!v[y]){//spfa优化
if(q.size()&&d[y]<d[q.front()])q.push_front(y);
else q.push_back(y);
v[y]=1;
}
}
}
}
return 1;
}
int main(){
int T;scanf("%d",&T);
while(T--){
scanf("%d%d",&n,&m);
for(int i=1;i<n;i++)for(int j=1;j<m;j++)scanf("%d",&b[i][j]);
memset(a,0,sizeof(a));
for(int i=n;i;i--)for(int j=m;j;j--)a[i][j]=b[i][j]-a[i][j+1]-a[i+1][j]-a[i+1][j+1];
for(int i=1;i<=n+m;i++)head[i]=0;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if((i+j)&1)add(i,j+n,1e6-a[i][j]),add(j+n,i,a[i][j]);
else add(i,j+n,a[i][j]),add(j+n,i,1e6-a[i][j]);
if(spfa()){
puts("YES");
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if((i+j)&1)a[i][j]+=d[j+n]-d[i];
else a[i][j]+=d[i]-d[j+n];
printf("%d ",a[i][j]);
}
puts("");
}
}else puts("NO");
}
return 0;
}
世界上有没有一种时间复杂度正确的判负环的方法?这题巨卡常,SPFA
卡不卡完全看心情,Bellman-Ford
常数太大了qwq。
SPFA
的优化听说也是假的,反向优化到指数级别qwq,能过一题是一题。