[acm/icpc2016ChinaFinal][CodeforcesGym101194] Mr.Panda and TubeMaster

这个题从范围来看,不难想到跟费用流有关系。但感觉跟费用流联系起来还是很难呀。拆点,这个题可以看成是给每个点找一个后继点,这一点是解题的关键。然后就是要黑白染色定向,定向这个点能走横边还是竖边,定向之后,神奇的发现所有的边都被连了有且仅有一次,这种套路估计大佬已经习以为常了吧。然后对于那些非限制格子,自己的入点连出点,保证可以不连接。然后直接看代码吧。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int MAXN = 2000+5;
const int inf = 1e9;
int n,m;
int s,e;

int cnt,head[MAXN];
struct node
{
    int u,v,w,f,next;
} edge[50000];

void init()
{
    cnt = 0;
    for(int i = 0; i <= e; ++i)head[i] = -1;
}

void add(int u,int v,int w,int f)
{
    edge[cnt].u = u;
    edge[cnt].v = v;
    edge[cnt].w = w;
    edge[cnt].f = f;
    edge[cnt].next = head[u];
    head[u] = cnt++;
    edge[cnt].u = v;
    edge[cnt].v = u;
    edge[cnt].w = -w;
    edge[cnt].f = 0;
    edge[cnt].next = head[v];
    head[v] = cnt++;
}

bool vis[MAXN];
int dis[MAXN],pre[MAXN];
bool spfa()
{
    for(int i = 0; i <= e; ++i)
    {
        dis[i] = -inf;
        pre[i] = -1;
    }
    queue<int>q;
    q.push(s);
    dis[s] = 0;
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        vis[u] = 0;
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            int v = edge[i].v;
            int w = edge[i].w;
            int f = edge[i].f;
            if(f > 0 && dis[v] < dis[u] + w)
            {
                dis[v] = dis[u] + w;
                pre[v] = i;
                if(!vis[v])
                {
                    vis[v] = 1;
                    q.push(v);
                }
            }
        }
    }
    if(pre[e] == -1)return 0;
    return 1;
}
void get_mincost()
{
    int max_flow = 0,min_cost = 0;
    while(spfa())
    {
        int p = pre[e];
        int flow = inf;
        while(p != -1)
        {
            flow = min(flow,edge[p].f);
            p = pre[edge[p].u];
        }
        max_flow += flow;
        min_cost += flow*dis[e];
        p = pre[e];
        while(p != -1)
        {
            edge[p].f -= flow;
            edge[p^1].f += flow;
            p = pre[edge[p].u];
        }
    }
    if(max_flow != n*m)puts("Impossible");
    else printf("%d\n",min_cost);
}

int scorec[40][40],scorer[40][40];
int L[40][40],R[40][40];
bool tu[40][40];

int main()
{
    int t,ca = 0;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);
        int id = 0;
        for(int i = 0; i < n; ++i)
            for(int j = 0; j < m; ++j)L[i][j] = ++id,R[i][j] = ++id;
        s = 0,e = ++id;
        for(int i = 0; i < n; ++i)
            for(int j = 0; j < m-1; ++j)scanf("%d",&scorec[i][j]);
        for(int i = 0; i < n-1; ++i)
            for(int j = 0; j < m; ++j)scanf("%d",&scorer[i][j]);
        int E;
        int x,y;
        scanf("%d",&E);
        while(E--)
        {
            scanf("%d%d",&x,&y);
            x--,y--;
            tu[x][y] = 1;
        }
        init();
        for(int i = 0; i < n; ++i)
            for(int j = 0; j < m; ++j)
            {
                if((i+j)%2)
                {
                    if(j+1 < m)add(L[i][j],R[i][j+1],scorec[i][j],1);
                    if(j-1 >= 0)add(L[i][j],R[i][j-1],scorec[i][j-1],1);
                }
                else
                {
                    if(i+1 < n)add(L[i][j],R[i+1][j],scorer[i][j],1);
                    if(i-1 >= 0)add(L[i][j],R[i-1][j],scorer[i-1][j],1);
                }
            }
        for(int i = 0; i < n; ++i)
            for(int j = 0; j < m; ++j)
            {
                add(s,L[i][j],0,1);
                add(R[i][j],e,0,1);
                if(!tu[i][j])add(L[i][j],R[i][j],0,1);
                else tu[i][j] = 0;
            }
        printf("Case #%d: ",++ca);
        get_mincost();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值