[网络流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;
    }

原文地址:https://www.cnblogs.com/jizhihong/p/13337338.html