发现每个点上下的管道只能选一条,左右管道只能选一条
而且管道连接的两个格子的奇偶性质一定不同,所以黑白染色
如何连边??考虑到必须成环这一点,发现只要满足每个点在竖直方向上选一条,在水平方向上选一条
就能成环,因为不存在终止点(每个点都能出去)
考虑把一个点拆分为两个点,原点和虚拟点
所以让白点负责横向的管道,分别向左右点的/虚拟点连边
所以让黑点负责纵向的管道,分别向上下点的虚拟点连边
源点 s s s向每个点的原点连流量 1 1 1费用 0 0 0的边
每个点的虚拟点向 t t t连流量 1 1 1费用 0 0 0的边
这样跑最小费用最大流,随后流量一定等于点数(因为一定存在连通 n ∗ m n*m n∗m个点)
然而非必须点可以不选啊…我们加一条边就可以了
让这个点的入点连向出点,流量为 1 1 1即可
这样如果不选这个点,就流这条边
这样其余点也一定是成环的。因为如果不成环,就不是最大流
所以连过边的点,原点被横向的管子连接,虚拟点被纵向的管道连接
满足条件。
贴一个别人的代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
using namespace std;
#define rep(i,a,n) for (int i=a;i<n;i++)
#define per(i,a,n) for (int i=n-1;i>=a;i--)
#define pb push_back
#define fi first
#define se second
typedef vector<int> VI;
typedef long long ll;
typedef pair<int,int> PII;
const int maxn=35+10;
int a[maxn][maxn],c[maxn][maxn],r[maxn][maxn];
const int MAXN = 2010;
const int MAXM = 20050;
const int INF = 0x3f3f3f3f;
struct Edge
{
int to,next,cap,flow,cost;
}edge[MAXM];
int head[MAXN],tol;
int pre[MAXN],dis[MAXN];
bool vis[MAXN];
int N;//节点总个数,节点编号从0~N-1
void init(int n)
{
N = n;
tol = 0;
rep(i,0,n) head[i]=-1;
}
void addedge(int u,int v,int cap,int cost)
{
edge[tol].to = v;edge[tol].cap = cap;edge[tol].cost = cost;edge[tol].flow = 0;
edge[tol].next = head[u];head[u] = tol++;
edge[tol].to = u;edge[tol].cap = 0;edge[tol].cost = -cost;edge[tol].flow = 0;
edge[tol].next = head[v];head[v] = tol++;
}
bool spfa(int s,int t)
{
queue<int> q;
for(int i = 0;i < N;i++)
{
dis[i] = INF;
vis[i] = false;
pre[i] = -1;
}
dis[s] = 0;
vis[s] = true;
q.push(s);
while(!q.empty())
{
int u = q.front();
q.pop();
vis[u] = false;
for(int i = head[u]; i != -1;i = edge[i].next)
{
int v = edge[i].to;
if(edge[i].cap > edge[i].flow &&
dis[v] > dis[u] + edge[i].cost )
{
dis[v] = dis[u] + edge[i].cost;
pre[v] = i;
if(!vis[v])
{
vis[v] = true;
q.push(v);
}
}
}
}
if(pre[t] == -1)return false;
else return true;
}
//返回的是最大流,cost存的是最小费用
int minCostMaxflow(int s,int t,int &cost)
{
int flow = 0;
cost = 0;
while(spfa(s,t))
{
int Min = INF;
for(int i = pre[t];i != -1;i = pre[edge[i^1].to])
{
if(Min > edge[i].cap - edge[i].flow)
Min = edge[i].cap - edge[i].flow;
}
for(int i = pre[t];i != -1;i = pre[edge[i^1].to])
{
edge[i].flow += Min;
edge[i^1].flow -= Min;
cost += edge[i].cost * Min;
}
flow += Min;
}
return flow;
}
int n,m;
int getl(int x,int y)
{
return (x-1)*m+y;
}
int main()
{
int cas;
scanf("%d",&cas);
int t=0;
while(cas--)
{
scanf("%d%d",&n,&m);
int st=0,ed=n*m*2+1;
rep(i,1,n+1) rep(j,1,m+1) a[i][j]=0;
rep(i,1,n+1) rep(j,1,m) scanf("%d",&c[i][j]);
rep(i,1,n) rep(j,1,m+1) scanf("%d",&r[i][j]);
int k;
scanf("%d",&k);
while(k--)
{
int x,y;
scanf("%d%d",&x,&y);
a[x][y]=1;
}
rep(i,1,n+1) rep(j,1,m+1) if(!a[i][j]) addedge(getl(i,j),n*m+getl(i,j),1,0);
rep(i,1,n+1)
{
rep(j,1,m+1)
{
if(((i+j)&1)==0) //改成 if((i+j)&1) 也是可以的
{
if(i<n) addedge(getl(i,j),n*m+getl(i+1,j),1,-r[i][j]);
if(i>1) addedge(getl(i,j),getl(i-1,j)+n*m,1,-r[i-1][j]);
}
else
{
if(j<m) addedge(getl(i,j),n*m+getl(i,j+1),1,-c[i][j]);
if(j>1) addedge(getl(i,j),n*m+getl(i,j-1),1,-c[i][j-1]);
}
addedge(st,getl(i,j),1,0);
addedge(n*m+getl(i,j),ed,1,0);
}
}
int cost=0;
int cnt=minCostMaxflow(st,ed,cost);
printf("Case #%d: ",++t);
if(cnt!=n*m) puts("Impossible");
else printf("%d\n",-cost);
}
return 0;
}