BZOJ 2668 [cqoi2012]交换棋子 | 最小费用最大流

传送门

BZOJ 2668

题解

同时分别限制流入和流出次数,所以把一个点拆成三个:入点in(x)、中间点mi(x)、出点ou(x)。

如果一个格子x在初始状态是黑点,则连(S, mi(x), 1, 0)
如果x在目标状态是黑点,则连(mi(x), T, 1, 0)
设x的交换次数限制是w
如果x在两种状态中颜色相同,则连(in(x), mi(x), w / 2, 0), (mi(x), ou(x), w / 2, 0)
如果x只在初始状态为黑色,则连(in(x), mi(x), w / 2, 0), (mi(x), ou(x), (w + 1) / 2, 0)
如果x只在目标状态为黑色,则连(in(x), mi(x), (w + 1) / 2, 0), (mi(x), ou(x), w / 2, 0)
每个点x和相邻的点y(八连通),连(ou(x), in(y), INF, 1)

然后最小费用最大流即可。

#include <queue>
#include <cstdio>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <iostream>
#define space putchar(' ')
#define enter putchar('\n')
typedef long long ll;
using namespace std;
template <class T>
void read(T &x){
    char c;
    bool op = 0;
    while(c = getchar(), c < '0' || c > '9')
        if(c == '-') op = 1;
    x = c - '0';
    while(c = getchar(), c >= '0' && c <= '9')
        x = x * 10 + c - '0';
    if(op) x = -x;
}
template <class T>
void write(T x){
    if(x < 0) putchar('-'), x = -x;
    if(x >= 10) write(x / 10);
    putchar('0' + x % 10);
}

const int N = 1234, M = 1000005, INF = 0x3f3f3f3f;
const int dx[] = {-1, 1, 0, 0, -1, -1, 1, 1};
const int dy[] = {0, 0, -1, 1, -1, 1, -1, 1};
int n, m, tot, maxflow, mincost, src = 1, des = 2, id[23][23][3], b1, b2;
int ecnt = 1, adj[N], pre[N], dis[N], nxt[M], go[M], cap[M], cost[M];
char st[23][23], ed[23][23], cp[23][23];

void _add(int u, int v, int w, int c){
    go[++ecnt] = v;
    nxt[ecnt] = adj[u];
    adj[u] = ecnt;
    cap[ecnt] = w;
    cost[ecnt] = c;
}
void add(int u, int v, int w, int c){
    _add(u, v, w, c);
    _add(v, u, 0, -c);
}
bool spfa(){
    queue <int> que;
    static bool inq[N] = {0};
    for(int i = 1; i <= tot; i++)
        dis[i] = INF, pre[i] = 0;
    dis[src] = 0, que.push(src), inq[src] = 1;
    while(!que.empty()){
        int u = que.front();
        que.pop(), inq[u] = 0;
        for(int e = adj[u], v; e; e = nxt[e]){
            if(cap[e] && dis[u] + cost[e] < dis[v = go[e]]){
                dis[v] = dis[u] + cost[e], pre[v] = e;
                if(!inq[v]) que.push(v), inq[v] = 1;
            }
        }
    }
    return dis[des] < INF;
}
void mcmf(){
    while(spfa()){
        int delta = INF;
        for(int e = pre[des]; e; e = pre[go[e ^ 1]])
            delta = min(delta, cap[e]);
        for(int e = pre[des]; e; e = pre[go[e ^ 1]])
            cap[e] -= delta, cap[e ^ 1] += delta;
        maxflow += delta;
        mincost += delta * dis[des];
    }
}
bool legal(int x, int y){
    return x > 0 && y > 0 && x <= n && y <= m;
}

int main(){

    read(n), read(m), tot = 3 * n * m + 2;
    for(int i = 1, cnt = 2; i <= n; i++)
        for(int j = 1; j <= m; j++)
            id[i][j][0] = ++cnt, id[i][j][1] = ++cnt, id[i][j][2] = ++cnt;
    for(int i = 1; i <= n; i++) scanf("%s", st[i] + 1);
    for(int i = 1; i <= n; i++) scanf("%s", ed[i] + 1);
    for(int i = 1; i <= n; i++) scanf("%s", cp[i] + 1);
    for(int i = 1; i <= n; i++)
        for(int j = 1; j <= m; j++){
            int w = cp[i][j] - '0';
            if(st[i][j] == '1') b1++, add(src, id[i][j][0], 1, 0);
            if(ed[i][j] == '1') b2++, add(id[i][j][0], des, 1, 0);
            if(st[i][j] == ed[i][j]){
                add(id[i][j][1], id[i][j][0], w / 2, 0);
                add(id[i][j][0], id[i][j][2], w / 2, 0);
            }
            else if(st[i][j] == '1'){
                add(id[i][j][1], id[i][j][0], w / 2, 0);
                add(id[i][j][0], id[i][j][2], (w + 1) / 2, 0);
            }
            else if(ed[i][j] == '1'){
                add(id[i][j][1], id[i][j][0], (w + 1) / 2, 0);
                add(id[i][j][0], id[i][j][2], w / 2, 0);
            }
            for(int d = 0, x, y; d < 8; d++)
                if(legal(x = i + dx[d], y = j + dy[d]))
                    add(id[i][j][2], id[x][y][1], INF, 1);
        }
    mcmf();
    if(maxflow < max(b1, b2)) puts("-1");
    else write(mincost), enter;

    return 0;
}

原文地址:https://www.cnblogs.com/RabbitHu/p/BZOJ2668.html

时间: 2024-08-27 04:44:29

BZOJ 2668 [cqoi2012]交换棋子 | 最小费用最大流的相关文章

【BZOJ-2668】交换棋子 最小费用最大流

2668: [cqoi2012]交换棋子 Time Limit: 3 Sec  Memory Limit: 128 MBSubmit: 1055  Solved: 388[Submit][Status][Discuss] Description 有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态.要求第i行第j列的格子只能参与mi,j次交换. Input 第一行包含两个整数n,m(1<=n, m<=20).以下n行为初始状态,每行为一个

BZOJ 1449 球队收益(最小费用最大流)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1449 题意: 思路:首先,我们假设后面的M场比赛两方都是输的,即初始时的lose[i]再加上i参加的场次.这样,后面对于i,每赢一场的收益增加值为: 之后win[i]++,lose[i]--.至此,我们得到建图的方法: (1)源点到每场比赛连流量1,费用0: (2)每场比赛向双方连流量1,费用0: (3)每个人到汇点连x条边(x为该人在M场比赛中出现的次数),流量1,费用为上面计算出的

BZOJ 1061 志愿者招募(最小费用最大流)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1061 题意:申奥成功后,布布经过不懈努力,终于 成为奥组委下属公司人力资源部门的主管.布布刚上任就遇到了一个难题:为即将启动的奥运新项目招募一批短期志愿者.经过估算,这个项目需要N 天才能完成,其中第i 天至少需要Ai 个人. 布布通过了解得知,一共有M 类志愿者可以招募.其中第i 类可以从第Si 天工作到第Ti 天,招募费用是每人Ci 元.新官上任三把火,为了出色地完成自己的工作,布

BZOJ 1221: [HNOI2001] 软件开发(最小费用最大流)

不知道为什么这么慢.... 费用流,拆点.... -------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<queue> #define rep( i, n ) for( int

BZOJ 1877: [SDOI2009]晨跑( 最小费用最大流 )

裸的费用流...拆点, 流量限制为1, 最后的流量和费用即答案. ---------------------------------------------------------------------- #include<bits/stdc++.h> using namespace std; const int maxn = 409; const int INF = 1 << 30; struct edge { int to, cap, cost; edge *next, *r

BZOJ 1927: [Sdoi2010]星际竞速(最小费用最大流)

拆点,费用流... ----------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<queue> #define rep( i, n ) for( int i = 0; i < n; +

BZOJ 1449: [JSOI2009]球队收益( 最小费用最大流)

先考虑假如全部输了的收益. 再考虑每场比赛球队赢了所得收益的增加量,用这个来建图.. -------------------------------------------------------------------------------------------------- #include<cstdio> #include<cstring> #include<algorithm> #include<queue> #include<iostre

BZOJ2668: [cqoi2012]交换棋子

题解: 可以戳这里:http://www.cnblogs.com/zig-zag/archive/2013/04/21/3033485.html 其实自己yy一下就知道这样建图的正确性了. 感觉太神奇,居然还能拆成3个点 orzzzzzzzzzzzzzzzzzzzzzzzzz 跪跪跪跪跪跪跪跪 代码: 1 #include<cstdio> 2 3 #include<cstdlib> 4 5 #include<cmath> 6 7 #include<cstring&

BZOJ 2668 交换棋子(费用流)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=2668 题意:有一个n行m列的黑白棋盘,你每次可以交换两个相邻格子中的棋子,最终达到目标状态.要求第i行第j列的格子只能参与m[i,j]次交换. 思路: 我们将1看做要移动的数字,将0看做空白.那么若1在始末状态个数不同则无解:如某个格子始末状态均有1则这个格子的1对结果无影响,可以将其都置为0.将每个格子拆为为个点p0,p1,p2: (1)若格子初始为1,则连边:<s,p0,1,0>