codechef BIBOARD

一个和标算不同的方法。
考虑二元关系网络流(最小割)。
根据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\to a\)不连边。
如果限制格子\((x,y)\)为白色,则\(b\to 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\n",ans-din());
	}
}
posted @ 2021-02-08 21:03  celerity1  阅读(83)  评论(0编辑  收藏  举报