一个和标算不同的方法。
考虑二元关系网络流(最小割)。
根据sdoi墙上的句子的经验,把每个网格上的点拆成两个,代表黑/白点。
给当前位置((x,y))新建两个点(a,b)。
a归到s->((x,y))选白色
a归到t->((x,y))选黑色
b归到s->((x,y))选黑色
b归到t->((x,y))选白色
考虑a->b连inf,这样子可以保证a,b不能同时选。
s->a连接流量为(a)格子的代价,b->t连接流量为(b)格子代价
如果限制格子((x,y))为黑色,则(s o a)不连边。
如果限制格子((x,y))为白色,则(b o t)不连边。
考虑矩形的限制。
把每个矩形新建两个点(c,d)。
如果我们为一个格子((x,y))赋值。
假设当前格子是白色,则包含它的黑矩形都不能选。
假设当前格子是黑色,则包含它的白矩形都不能选。
s->c,d->t连代价的边(如果当前矩形包含不是它颜色的点,则不连)。
根据前面的经验,((x,y))对应的点(a,b),d->b连inf,a->c连inf。
c->d连inf,表示c和d不能同时选
发现这样子跑出来的是“最小要减少的代价”
设变量(ans),初始为(0)。
考虑每个格子,如果它必须是黑色,则ans+=它为黑色时的代价。
如果它必须是白色,则ans+=它为白色时的代价。
否则ans+=它为白色时的代价+它为黑色时的代价
对于每个矩形,如果这个矩形和t/s有边,则ans+=这个矩形的代价
ans-最小割就是答案。
(上面的做法错了)
还是考虑最小割。
对于每个格子建立一个点,如果这个点被归到s集合就是白色,否则是黑色。
考虑对于每个正方形拆成2个点((a,b)),表示这个矩形为黑/白。
a归到s->正方形选白色
a归到t->正方形选黑色
b归到s->正方形选黑色
b归到t->正方形选白色
把a对格子对应的点连接inf,格子对应的点和b连inf。
如果正方形能够选白色,则s->a连接权值为代价的边,ans+=代价。
如果正方形能够选黑色,则b->t连接权值为代价的边,ans+=代价。
ans-最小割就是答案。
#include<bits/stdc++.h>
using namespace std;
#define N 1000010
#define int long long
int T;
int h[N],nxt[N],v[N],w[N],s,t,dep[N],ec,ans,v1[N],v2[N],inf=1e10,n,m,id[510][510],st[N],tp;
char c[1000][1000];
void add(int x,int y,int z){
v[++ec]=y;
w[ec]=z;
nxt[ec]=h[x];
h[x]=ec;
}
void adj(int x,int y,int z){
add(x,y,z);
add(y,x,0);
}
bool bfs(){
queue<int>q;
q.push(s);
for(int i=1;i<=ec;i++)
dep[i]=0;
dep[s]=1;
while(!q.empty()){
int x=q.front();q.pop();
for(int i=h[x];i;i=nxt[i])
if(w[i]&&!dep[v[i]]){
dep[v[i]]=dep[x]+1;
q.push(v[i]);
if(v[i]==t)
return 1;
}
}
return 0;
}
int dfs(int x,int dis){
if(x==t)return dis;
int tp=dis;
for(int i=h[x];i;i=nxt[i])
if(dep[v[i]]==dep[x]+1&&w[i]){
int f=dfs(v[i],min(tp,w[i]));
if(!f)
dep[v[i]]=0;
tp-=f;
w[i]-=f;
w[i^1]+=f;
if(!tp)
break;
}
return dis-tp;
}
int din(){
int aans=0;
while(bfs()){
int v;
while(v=dfs(s,inf))
aans+=v;
}
return aans;
}
signed main(){
scanf("%lld",&T);
while(T--){
scanf("%lld%lld",&n,&m);
ec=1;
ans=0;
memset(h,0,sizeof(h));
for(int i=1;i<=n;i++)
scanf("%s",c[i]+1);
for(int i=1;i<=min(n,m);i++)
scanf("%lld",&v1[i]);
for(int i=1;i<=min(n,m);i++)
scanf("%lld",&v2[i]);
s=1;
t=2;
int cc=2;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
if(c[i][j]=='?')
id[i][j]=++cc;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
for(int k=1;k<=min(i,j);k++){
int d[3]={0,0,0};
tp=0;
for(int a=i-k+1;a<=i;a++)
for(int b=j-k+1;b<=j;b++){
if(c[a][b]=='?'){
d[2]++;
st[++tp]=id[a][b];
}
if(c[a][b]=='0')
d[0]++;
if(c[a][b]=='1')
d[1]++;
}
if(!tp){
if(d[0]==k*k)
ans+=v1[k];
if(d[1]==k*k)
ans+=v2[k];
}
else{
if(d[0]+d[2]==k*k){
cc++;
ans+=v1[k];
for(int l=1;l<=tp;l++)
adj(cc,st[l],inf);
adj(s,cc,v1[k]);
}
if(d[1]+d[2]==k*k){
cc++;
ans+=v2[k];
for(int l=1;l<=tp;l++)
adj(st[l],cc,inf);
adj(cc,t,v2[k]);
}
}
}
printf("%lld
",ans-din());
}
}