zoukankan      html  css  js  c++  java
  • 洛谷P3159 交换棋子 神奇的网络流

    神奇的建模。。。原题链接

    如果你真的把交换看成交换,就(GG)了。首先我们要把交换看成是白棋的移动。

    然后,很容易的就想到建模的大致思路:建立超级源点S和超级汇点T,从S向初始局面每个白棋所在的格子连边,从目标局面每个白棋所在的格子向T连边,在相邻的格子中间加一些有限制的边,跑一波费用流。

    那中间的那些边应该怎么加呢,先考虑把每个格子拆成两个点,中间连一条以交换次数为流量上限的边。但是这样会有问题,考虑某个白棋移动路径上的点,其实只有开头的格子交换了一次,中间的都是两次。只拆两个体现不了在中间或首尾的差别,那我们就拆三个。

    把一个格子拆成三个点:TYPE1TYPE2TYPE3,分别表示流入,当前格子,流出。与ST的连边改为向TYPE2连边,与相邻格子的连边在TYPE1TYPE3之间连,另外从TYPE2TYPE1TYPE3连边,这样就能体现在路径上位置的差别了。

    那这些边的容量和费用该怎么设置呢?
    1.TYPE1TYPE3之间的连边容量为(INF),费用为(1)
    2.STYPE2TYPE2T容量为(1),费用为(0)
    3.TYPE2TYPE1TYPE3的连边(重点):
    对于某个格子(假设它的交换上限为(w),"/"号代表整除):
    ①若初始为白棋,目标为黑棋,则从TYPE1TYPE2连一条容量为(w/2),费用为(0)的边,从TYPE2TYPE3连一条容量为((w+1)/2),费用为(0)的边
    ②若初始为黑棋,目标为白棋,则从TYPE1TYPE2连一条容量为((w+1)/2),费用为(0)的边,从TYPE2TYPE3连一条容量为(w/2),费用为(0)的边
    ③若始末颜色相同,则从TYPE1TYPE2连一条容量为(w/2),费用为(0)的边,从TYPE2TYPE3连一条容量为(w/2),费用为(0)的边
    为什么权值要这样设,因为我们要保持收支平衡,使得每个格子在交换之后的颜色是对的。还有我们考虑的是白棋的移动,因此①应该会多一次流出,于是我们尽量把零头分给TYPE2TYPE3之间的那条边。②同理。

    注意判(-1)

    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define pii pair<int, int>
    #define mp make_pair
    #define pb push_back
    #define N 10000
    #define INF 0x3f3f3f3f
    
    int n, m, S, T;
    
    struct MCMF
    {
        struct Edge
        {
            int from, to, cap, flow, cost;
        };
        int S, T;
        int d[N+5], a[N+5], vis[N+5], pre[N+5];
        vector<int> G[N+5];
        vector<Edge> edges;
        void init(int S, int T)
        {
            this->S = S, this->T = T;
        }
        void addEdge(int from, int to, int cap, int cost)
        {
            edges.pb(Edge{from, to, cap, 0, cost}), edges.pb(Edge{to, from, 0, 0, -cost});
            G[from].pb(edges.size()-2), G[to].pb(edges.size()-1);
        }
        int SPFA(int &flow, int &cost)
        {
            memset(d, 0x3f, sizeof d), memset(vis, 0, sizeof vis);
            d[S] = 0, a[S] = INF, vis[S] = 1, pre[S] = 0;
            queue<int> q;
            q.push(S);
            while(!q.empty())
            {
                int u = q.front(); q.pop();
                vis[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;
                        pre[e.to] = G[u][i];
                        a[e.to] = min(a[u], e.cap-e.flow);
                        if(!vis[e.to]) vis[e.to] = 1, q.push(e.to);
                    }
                }
            }
            if(d[T] == INF) return 0;
            int u = T;
            flow += a[T], cost += d[T]*a[T];
            while(u != S)
            {
                edges[pre[u]].flow += a[T], edges[pre[u]^1].flow -= a[T];
                u = edges[pre[u]].from;
            }
            return 1;
        }
        pii minCost()
        {
            int flow = 0, cost = 0;
            while(SPFA(flow, cost));
            return mp(flow, cost);
        }
    }solver;
    
    int Val(char c)
    {
        return c-'0';
    }
    
    int TYPE1(int i, int j)
    {
        return ((i-1)*m+j)*3-2;
    }
    
    int TYPE2(int i, int j)
    {
        return ((i-1)*m+j)*3-1;
    }
    
    int TYPE3(int i, int j)
    {
        return ((i-1)*m+j)*3;
    }
    
    int a[25][25], b[25][25], w[25][25];
    int d[8][2] = {-1,0,1,0,0,-1,0,1,-1,-1,-1,1,1,-1,1,1};
    //0:black 1:white
    int main()
    {
        scanf("%d%d", &n, &m);
        S = 0, T = 3*n*m+1;
        solver.init(S, T);
        char c;
        int sum = 0;
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= m; ++j) cin >> c, a[i][j] = Val(c), sum += a[i][j];
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= m; ++j) cin >> c, b[i][j] = Val(c);
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= m; ++j) cin >> c, w[i][j] = Val(c);
        for(int i = 1; i <= n; ++i)
            for(int j = 1; j <= m; ++j)
            {
                if(a[i][j]) solver.addEdge(S, TYPE2(i, j), 1, 0);
                if(b[i][j]) solver.addEdge(TYPE2(i, j), T, 1, 0);
                if(a[i][j] == 0 && b[i][j] == 1)
                    solver.addEdge(TYPE1(i, j), TYPE2(i, j), (w[i][j]+1)/2, 0), solver.addEdge(TYPE2(i, j), TYPE3(i, j), w[i][j]/2, 0);
                else if(a[i][j] == 1 && b[i][j] == 0)
                    solver.addEdge(TYPE1(i, j), TYPE2(i, j), w[i][j]/2, 0), solver.addEdge(TYPE2(i, j), TYPE3(i, j), (w[i][j]+1)/2, 0);
                else
                    solver.addEdge(TYPE1(i, j), TYPE2(i, j), w[i][j]/2, 0), solver.addEdge(TYPE2(i, j), TYPE3(i, j), w[i][j]/2, 0);
                for(int k = 0, ti, tj; k < 8; ++k)
                {
                    ti = i+d[k][0], tj = j+d[k][1];
                    if(ti < 1 || ti > n || tj < 1 || tj > m) continue;
                    solver.addEdge(TYPE3(i, j), TYPE1(ti, tj), INF, 1);
                }
            }
        pii res = solver.minCost();
        if(res.first != sum) printf("-1
    ");
        else printf("%d
    ", res.second);
        return 0;
    }
    
  • 相关阅读:
    1104 Sum of Number Segments (20 分)(数学问题)
    1092 To Buy or Not to Buy (20 分)(hash散列)
    1082 Read Number in Chinese (25 分)(字符串处理)【背】
    1105 Spiral Matrix (25 分)(模拟)
    初识网络安全及搭建网站(内网)
    HTML5开发者需要了解的技巧和工具汇总(转)
    native+web开发模式之web前端经验分享
    移动平台3G手机网站前端开发布局技巧汇总(转)
    Asp.net 中图片存储数据库以及页面读取显示通用方法详解附源码下载
    使用H3Viewer来查看VS2010的帮助文档
  • 原文地址:https://www.cnblogs.com/dummyummy/p/10112732.html
Copyright © 2011-2022 走看看