zoukankan      html  css  js  c++  java
  • [网络流24题] 骑士共存问题 (二分图匹配 最大流)

    题目链接

    其实做这种棋盘上的题目,其实可以考虑,把棋盘按黑白间隔分成两部分
    这里,对于 (i,j) 如果将 (i+j)%2 == 1 定义为奇数位 , 反之就是偶数位
    比较有趣的是,奇数位的骑士能走一步能到达的位置一定是偶数位,同样,偶数位能到达的位置一定是奇数位
    所以,就可以把奇数位,偶数位放两侧,将走一步能到达的点连起来,形成一个二分图
    题目要求的答案就是 总点数-障碍点-二分图的最大匹配

    #include <bits/stdc++.h>
    #define pos(x, y) (x-1)*n+y
    const int maxn = 205;
    using namespace std;
    typedef long long ll;
    int xiang[8][2] = {{1,2},{1,-2},{-1,2},{-1,-2},{2,1},{2,-1},{-2,1},{-2,-1}};
    vector <int> maps[maxn*maxn];
    int bar[maxn*maxn], mark[maxn*maxn];
    int pipei[maxn*maxn];
    int n, m;
    
    bool dfs(int u){
        for(int i=0, len=maps[u].size(); i<len; i++){
            int v = maps[u][i];
            if(!mark[v]){
                mark[v] = true;
                if(!pipei[v] || dfs(pipei[v])){
                    pipei[v] = u;
                    return true;
                }
            }
        }
        return false;
    }
    
    int main()
    {
        scanf("%d%d", &n, &m);
        for(int i=1; i<=m; i++){
            int a, b;
            scanf("%d%d", &a, &b);
            bar[pos(a, b)] = true;
        }
        for(int i=1; i<=n; i++){
            for(int j=1; j<=n; j++){
                int u = pos(i, j);
                if((i+j)%2 && !bar[u]){   //该点是奇数点,而且没有障碍
                    for(int d=0; d<8; d++){
                        int tx = i + xiang[d][0];
                        int ty = j + xiang[d][1];
                        int v = pos(tx, ty);
                        if(tx < 1 || ty < 1 || tx > n || ty > n || bar[v])
                            continue;//越界或者有障碍
                        maps[u].push_back(v);
                        maps[v].push_back(u);
                    }
                }
            }
        }
        int ans = 0;
        for(int i=1; i<=n; i++){
            for(int j=1; j<=n; j++){
                int x = pos(i, j);
                if((i+j)%2 && !bar[x]){ //奇数位匹配偶数位
                    memset(mark, false, sizeof(mark));
                    if(dfs(x))
                        ans++;
                }
            }
        }
        printf("%d
    ", n*n - m - ans);  //总点数 - 障碍数 - 最大匹配
        return 0;
    }
    
    

    同样最大流也可以计算出二分图的最大匹配

        #include <bits/stdc++.h>
        #define pos(x, y) (x-1)*n +  y
        const int maxn = 205;
        const int inf = 0x3f3f3f3f;
        using namespace std;
        int xiang[8][2] = {{1,2},{1,-2},{-1,2},{-1,-2},{2,1},{2,-1},{-2,1},{-2,-1}};
        struct note{
            int u, v, w;
            int next;
        } e[maxn*maxn*20];
        int head[maxn*maxn], cnt;
        int n, m, s, t;
        bool bar[maxn][maxn];
    
        void add(int u, int v, int w){
            e[cnt] = (note){u, v, w, head[u]};
            head[u] = cnt++;
            e[cnt] = (note){v, u, 0, head[v]};
            head[v] = cnt++;
        }
        int level[maxn*maxn];
        bool bfs(){
            memset(level, -1, sizeof(level));
            level[s] = 1;
            queue<int> q;
            q.push(s);
            while(!q.empty()){
                int u = q.front();
                q.pop();
                for(int i=head[u]; i; i=e[i].next){
                    int v = e[i].v;
                    if(e[i].w > 0 && level[v] == -1){
                        level[v] = level[u] + 1;
                        q.push(v);
                    }
                }
            }
            return level[t] != -1;
        }
    
        int dfs(int u, int delta){
            if(u == t)
                return delta;
            int flow = 0, tmp;
            for(int i=head[u]; i; i=e[i].next){
                int v = e[i].v;
                if(e[i].w > 0 && level[u] + 1 == level[v]){
                    tmp = dfs(v, min(delta-flow, e[i].w));
                    e[i].w -= tmp;
                    e[i^1].w += tmp;
                    flow += tmp;
                    if(flow == delta)
                        return flow;
                }
            }
            if(flow == 0)
                level[u] = -1;
            return flow;
        }
    
        int Dinic(){
            int maxflow = 0, tmp;
            while(bfs()){
                while((tmp=dfs(s, inf)))
                    maxflow += tmp;
            }
            return maxflow;
        }
    
        int main()
        {
            scanf("%d%d", &n, &m);
            s = 0, t = n*n+1;
            cnt = 2;
            for(int i=0; i<m; i++){
                int a, b;
                scanf("%d%d", &a, &b);
                bar[a][b] = true;
            }
            for(int i=1; i<=n; i++){
                for(int j=1; j<=n; j++){
                    if((i+j)%2 && !bar[i][j]){
                        int u = pos(i, j);
                        add(s, u, 1);   //源点 --> 奇数位
                        for(int d=0; d<8; d++){
                            int tx = i + xiang[d][0];
                            int ty = j + xiang[d][1];
                            if(tx < 1 || ty < 1 || tx > n || ty > n || bar[tx][ty])
                                continue;//越界或者有障碍
                            add(u, pos(tx, ty), 1);   //奇数位 --> 偶数位
                        }
                    }
                    else if((i+j)%2 == 0 && !bar[i][j])
                        add(pos(i, j), t, 1);   //偶数位 --> 汇点
                }
            }
            printf("%d
    ", n*n-m-Dinic());
            return 0;
        }
    
    
  • 相关阅读:
    2020-08-20
    2020-08-19
    2020-08-14
    2020-08-13
    使用numpy实现机器学习模型
    分治法学习
    2020-08-09
    2020-08-02
    四月是你的谎言下载
    新博客
  • 原文地址:https://www.cnblogs.com/jizhihong/p/13337338.html
Copyright © 2011-2022 走看看