题意:给你一个n*m的网格,部分网格有数字,要求奇数的数字要和偶数的数字连在一起,空白的数字要成环,给你每个格子连接上下左右的费用,求满足条件最小费用
思路:考虑费用流,讲每个点拆点,源点S连向所有奇数点的入点,T连向所有偶数的出点,然后S连所有空白的入点,出点连T,上下左右的入点连向各自的出点,这样跑一次最小费用最大流即可
#include<bits/stdc++.h>
using namespace std;
const int maxn = 5005;
#define INF 1e9
int tot;
struct Edge
{
int from,to,cap,flow,cost;
Edge(){};
Edge(int f,int t,int c,int fl,int co):from(f),to(t),cap(c),flow(fl),cost(co){}
};
struct MCMF
{
int n,m,s,t;
vector<Edge>edges;
vector<int>g[maxn];
bool inq[maxn];
int d[maxn];
int p[maxn];
int a[maxn];
void init(int n,int s,int t)
{
this->n=n;
this->s=s;
this->t=t;
edges.clear();
for(int i = 0;i<=n;i++)g[i].clear();
}
void AddEdge(int from,int to,int cap,int cost)
{
edges.push_back(Edge(from,to,cap,0,cost));
edges.push_back(Edge(to,from,0,0,-cost));
m=edges.size();
g[from].push_back(m-2);
g[to].push_back(m-1);
}
bool BellmanFord(int &flow,int &cost)
{
for(int i = 0;i<n;i++)d[i]=INF;
memset(inq,0,sizeof(inq));
d[s]=0,a[s]=INF,inq[s]=1,p[s]=0;
queue<int>q;
q.push(s);
while(!q.empty())
{
int u = q.front();
q.pop();
inq[u]=0;
for(int i = 0;i<g[u].size();i++)
{
Edge &e = edges[g[u][i]];
if(e.cap > e.flow && d[e.to]>d[u]+e.cost)
{
d[e.to]=d[u]+e.cost;
p[e.to]=g[u][i];
a[e.to]=min(a[u],e.cap-e.flow);
if(!inq[e.to])
{
q.push(e.to);
inq[e.to]=1;
}
}
}
}
if(d[t]==INF)return false;
flow+=a[t];
cost+=a[t]*d[t];
int u = t;
while(u!=s)
{
edges[p[u]].flow+=a[t];
edges[p[u]^1].flow-=a[t];
u=edges[p[u]].from;
}
return true;
}
void Min_cost(int &flow,int &cost)
{
flow=0;cost=0;
while(BellmanFord(flow,cost));
//return cost;
}
}mc;
int main()
{
int T,cas=1;
scanf("%d",&T);
while(T--)
{
tot=0;
int n,m;
scanf("%d%d",&n,&m);
int s = n*m*2+1;
int t = n*m*2+2;
mc.init(n*m*2+3,n*m*2+1,n*m*2+2);
for(int i = 1;i<=n;i++)
for(int j = 1;j<=m;j++)
{
int x;
scanf("%d",&x);
if(x==0)
{
mc.AddEdge(s,(i-1)*m+j,1,0); //s向为0的格子的入点建边
mc.AddEdge((i-1)*m+j+n*m,t,1,0); //0的格子的出点向t建边
tot++;
}
else if (x&1)
mc.AddEdge(s,(i-1)*m+j,1,0),tot++; //s向奇数格子的入点建边
else
mc.AddEdge((i-1)*m+j+n*m,t,1,0); //偶数格子的出边向t建边
}
for(int i = 2;i<=n;i++)
for(int j = 1;j<=m;j++)
{
int x;
scanf("%d",&x);
int l = i-1;
mc.AddEdge((l-1)*m+j,(i-1)*m+j+n*m,1,x); //上一个点的入边向下一个点的出边建边
mc.AddEdge((i-1)*m+j,(l-1)*m+j+n*m,1,x);
}
for(int i =1;i<=n;i++)
for(int j = 2;j<=m;j++)
{
int x;
scanf("%d",&x);
int l = j-1;
mc.AddEdge((i-1)*m+l,(i-1)*m+j+n*m,1,x);
mc.AddEdge((i-1)*m+j,(i-1)*m+l+n*m,1,x);
}
int flow,cost;
mc.Min_cost(flow,cost);
printf("Case #%d: ",cas++);
if(flow<tot)
printf("-1\n");
else
printf("%d\n",cost);
}
}