【Gym】101194J Mr.Panda and TubeMaster(二分图费用流)

LINK

发现每个点上下的管道只能选一条,左右管道只能选一条

而且管道连接的两个格子的奇偶性质一定不同,所以黑白染色

如何连边??考虑到必须成环这一点,发现只要满足每个点在竖直方向上选一条,在水平方向上选一条

就能成环,因为不存在终止点(每个点都能出去)

考虑把一个点拆分为两个点,原点和虚拟点

所以让白点负责横向的管道,分别向左右点的/虚拟点连边

所以让黑点负责纵向的管道,分别向上下点的虚拟点连边

源点 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;
}
上一篇:P3931一道难题 Tree(树形dp/最小割)


下一篇:CF1045A Last chance 题解