如果我们知道了要变成的数
d
,怎么检验这个答案可不可行。
将整张棋盘黑白染色,黑点连向四边的白点,容量为无穷大,源点连向黑点,容量为
那么当
m∗n
为偶数时,可以二分答案。
奇数时,不妨设黑格子比白格子多一个,不管怎么操作,黑格子的和与白格子的和的差是定值,设为
dif
,再设最后变成的数为
d
,有:
⇒d=dif
,网络流验证即可。
#include<ctime>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<cassert>
#include<cmath>
#include<vector>
#include<queue>
#include<stack>
#include<map>
#include<climits>
#define X first
#define Y second
#define DB double
#define lc now<<1
#define rc now<<1|1
#define MP make_pair
#define LL long long
#define pb push_back
#define sqr(_) ((_)*(_))
#define INF 0x3f3f3f3f
#define pii pair<int,int>
#define pdd pair<DB,DB>
#define ull unsigned LL
#define int LL
#define DEBUG(...) fprintf(stderr,__VA_ARGS__)
using namespace std;
const int MAXN=44;
int ans=0,n,m,T,w[MAXN][MAXN],d,s1,s2,id[MAXN][MAXN],tot=0,color[MAXN][MAXN],_w[MAXN*MAXN];
struct Edge{
int u,v,cap,flow;
Edge(int u=0,int v=0,int cap=0,int flow=0):u(u),v(v),cap(cap),flow(flow){}
};
struct Dinic
{
int S,T;
vector<int> G[MAXN*MAXN];
vector<Edge> edges;
bool vis[MAXN*MAXN];
int dis[MAXN*MAXN],cur[MAXN*MAXN];
void init()
{
for(int i=0;i<MAXN*MAXN;i++)
G[i].clear();
edges.clear();
}
void add(int a,int b,int w)
{
edges.pb(Edge(a,b,w,0));
edges.pb(Edge(b,a,0,0));
int k=edges.size();
G[a].pb(k-2);
G[b].pb(k-1);
}
bool BFS()
{
memset(vis,0,sizeof(vis));
queue<int> q;
q.push(S);
vis[S]=1;
memset(dis,INF,sizeof(dis));
dis[S]=0;
while(!q.empty())
{
int u=q.front();q.pop();
for(int i=0;i<G[u].size();i++)
{
Edge& e=edges[G[u][i]];
if(!vis[e.v]&&e.cap>e.flow)
{
vis[e.v]=1;
dis[e.v]=dis[u]+1;
q.push(e.v);
}
}
}
return vis[T];
}
int DFS(int u,int a)
{
if(u==T||a==0)return a;
int f=0,flow=0;
for(int& i=cur[u];i<G[u].size();i++)
{
Edge& e=edges[G[u][i]];
if(dis[e.v]==dis[u]+1&&((f=DFS(e.v,min(a,e.cap-e.flow)))>0))
{
flow+=f;
e.flow+=f;
edges[G[u][i]^1].flow-=f;
a-=f;
if(a==0)break;
}
}
return flow;
}
int MaxFlow()
{
int res=0;
while(BFS())
{
memset(cur,0,sizeof(cur));
res+=DFS(S,LONG_LONG_MAX/10);
}
return res;
}
bool rebuild(int mid)
{
for(int i=0;i<edges.size();i++)
{
Edge& e=edges[i];
e.flow=0;
if(e.v==T)
{
e.cap=mid-_w[e.u];
if(e.cap<0)return 0;
}
else if(e.u==S)
{
e.cap=mid-_w[e.v];
if(e.cap<0)return 0;
}
}
return 1;
}
bool check(int mid)
{
if(!rebuild(mid))return 0;
ans=MaxFlow();
for(int i=0;i<edges.size();i++)
{
//DEBUG("%I64d %I64d %I64d %I64d\n",edges[i].u,edges[i].v,edges[i].cap,edges[i].flow);
if(edges[i].v==T&&edges[i].cap!=edges[i].flow)
return 0;
}
return 1;
}
}Graph;
void colored()
{
for(int i=1;i<=n;i++)
{
if(i&1)
{
for(int j=1;j<=m;j++)
if(j&1)
color[i][j]=1;
}
else
{
for(int j=1;j<=m;j++)
if(!(j&1))
color[i][j]=1;
}
}
}
void solve1()
{
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(color[i][j])
s1+=w[i][j];
else
s2+=w[i][j];
d=abs(s1-s2);
if(Graph.check(d))
printf("%lld\n",ans);
else
puts("-1");
}
void solve2()
{
int l=0,r=10000000000000000;
if(!Graph.check(r))
{
puts("-1");
return;
}
while(l+1!=r)
{
int mid=(l+r)>>1;
if(Graph.check(mid))
r=mid;
else
l=mid;
}
Graph.check(r);
printf("%lld\n",ans);
}
void init()
{
Graph.init();
ans=s1=s2=tot=0;
scanf("%lld %lld",&n,&m);
memset(color,0,sizeof(color));
memset(w,0,sizeof(w));
colored();
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
{
scanf("%lld",&w[i][j]);
id[i][j]=++tot;
_w[tot]=w[i][j];
}
Graph.S=++tot,Graph.T=++tot;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(color[i][j])
{
Graph.add(Graph.S,id[i][j],0);
if(w[i-1][j])
Graph.add(id[i][j],id[i-1][j],LONG_LONG_MAX);
if(w[i+1][j])
Graph.add(id[i][j],id[i+1][j],LONG_LONG_MAX);
if(w[i][j+1])
Graph.add(id[i][j],id[i][j+1],LONG_LONG_MAX);
if(w[i][j-1])
Graph.add(id[i][j],id[i][j-1],LONG_LONG_MAX);
}
else
Graph.add(id[i][j],Graph.T,0);
if((n*m)&1)
solve1();
else
solve2();
}
main()
{
#ifndef ONLINE_JUDGE
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
#endif
scanf("%lld",&T);
while(T--)
{
init();
}
}