zoukankan      html  css  js  c++  java
  • [CQOI2012]交换棋子【网络流】【费用流】

    题目描述

    有一个nm列的黑白棋盘,你每次可以交换两个相邻格子(相邻是指有公共边或公共顶点)中的棋子,最终达到目标状态。要求第i行第j列的格子只能参与mi,j次交换。

    输入输出格式

    输入格式:

    第一行包含两个整数nm(1n,m20)。以下n行为初始状态,每行为一个包含m个字符的01串,其中0表示黑色棋子,1表示白色棋子。以下n行为目标状态,格式同初始状态。以下n行每行为一个包含m0..9数字的字符串,表示每个格子参与交换的次数上限。

    输出格式:

    输出仅一行,为最小交换总次数。如果无解,输出1

    解法:

    一眼费用流系列…然而这题的处理有点诡异,首先要拆点将点的限制转化为边的限制,SINi,OUTiTINOUT的权值是多少?分三种情况讨论:

    1. 既不是起始也不是结束状态:n2
    2. 既是起始也是结束状态:n+22
    3. 是起始或结束状态:n+12
    #include <bits/stdc++.h>
    using namespace std;
    
    const int MAXN = 2005;
    struct node {
        int to, next, f, c, neg;
    } edge[MAXN*40];
    int head[MAXN], top = 0;
    void push(int i, int j, int k, int l)
    {
        if (i*j == 0) return;
        ++top, edge[top] = (node) {j, head[i], k, l, top+1}, head[i] = top;
        ++top, edge[top] = (node) {i, head[j], 0, -l, top-1}, head[j] = top;
    }
    
    int vis[MAXN], dis[MAXN], S = 2001, T = 2002;
    queue<int> que;
    int pre[MAXN], pre_edge[MAXN];
    const int INF = 233333333;
    
    bool spfa()
    {
        memset(dis, 127/3, sizeof dis);
        memset(pre, 0, sizeof pre);
        vis[S] = 1, que.push(S), dis[S] = 0;
        while (!que.empty()) {
            int tp = que.front(); que.pop(), vis[tp] = 0;
            for (int i = head[tp]; i; i = edge[i].next) {
                if (edge[i].f == 0 || dis[edge[i].to] <= dis[tp] + edge[i].c) continue;
                int to = edge[i].to, c = edge[i].c;
                dis[to] = dis[tp] + c, pre[to] = tp, pre_edge[to] = i;
                if (!vis[to])
                    vis[to] = 1, que.push(to);
            }
        }
        return dis[T] < INF;
    }
    
    int sap(int &cost)
    {
        int ans = INF;
        for (int i = T; i != S; i = pre[i]) ans = min(ans, edge[pre_edge[i]].f);
        for (int i = T; i != S; i = pre[i]) edge[pre_edge[i]].f -= ans, edge[edge[pre_edge[i]].neg].f += ans;
        cost += ans*dis[T];
        return ans;
    }
    
    int mst(int &cost)
    {
        cost = 0;
        int ans = 0;
        while (spfa()) ans += sap(cost);
        return ans;
    }
    
    int n, m;
    int A[30][30], B[30][30], C[30][30];
    char str[30];
    
    int readln(int A[30][30])
    {
        int cnt = 0;
        for (int i = 1; i <= n; i++) {
            scanf("%s", str+1);
            for (int j = 1; j <= m; j++)
                A[i][j] = str[j]-'0', cnt += A[i][j];
        }
        return cnt;
    }
    
    inline int number(int i, int j, int id)
    { return (i<=0||i>n||j<=0||j>m)?0:id*n*m+(i-1)*m+j; }
    
    int dx[] = {1,0,-1,0, 1, 1, -1, -1}, dy[] = {0,1,0,-1, -1, 1, -1, 1};
    int main()
    {
        scanf("%d%d", &n, &m);
        int a = readln(A);
        int b = readln(B); readln(C);
        if (a != b) {puts("-1"); return 0;}
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++) {
                if (A[i][j] == 1) push(S, number(i, j, 0), 1, 0);
                if (B[i][j] == 1) push(number(i, j, 1), T, 1, 0);
                if (A[i][j] == 1 && B[i][j] == 1) push(number(i, j, 0), number(i, j, 1), (C[i][j]+2)/2, 1);
                else if (A[i][j] == 1 || B[i][j] == 1) push(number(i, j, 0), number(i, j, 1), (C[i][j]+1)/2, 1);
                else push(number(i, j, 0), number(i, j, 1), C[i][j]/2, 1);
                for (int k = 0; k < 8; k++) push(number(i, j, 1), number(i+dx[k], j+dy[k], 0), INF, 0);
            }
        int ans = 0, cost = 0;
        ans = mst(cost);
        if (ans == a) {
            cout << cost-a << endl;
        } else
            puts("-1");
        return 0;
    }
  • 相关阅读:
    又发现新的QQ系统消息相关dll文件
    修改QQ版本号,解决QQ版本过低
    学DIV+CSS技术,如何入门?
    删除MSN广告及屏蔽MSN网页广告的方法
    制作网页需要掌握的几个基本技能
    陈晓薇的灿烂人生:比美丽更动人
    屏蔽QQ系统广播消息方法
    HTML结构化CSS网页布局入门指南
    Photoshop常用快捷键(逐步熟悉)
    如何去除视频上的水印?
  • 原文地址:https://www.cnblogs.com/ljt12138/p/6684333.html
Copyright © 2011-2022 走看看