网络流建模方法(四)互不攻击问题,或者说是共存问题, 这类题目看起来有点像二分图匹配,这类题目我们就是建一个二分图然后跑最大流
还是先说题目洛谷P3353
题目描述
在一个 nn个方格的国际象棋棋盘上,马(骑士)可以攻击的棋盘方格如图所示。棋盘上某些方格设置了障碍,骑士不得进入
对于给定的 n
n 个方格的国际象棋棋盘和障碍标志,计算棋盘上最多可以放置多少个骑士,使得它们彼此互不攻击
输入格式
第一行有 2 个正整数n 和 m (1<=n<=200, 0<=m<n2),分别表示棋盘的大小和障碍数。接下来的 m 行给出障碍的位置。每行 2 个正整数,表示障碍的方格坐标。
输出格式
将计算出的共存骑士数输出
输入
3 2
1 1
3 3
输出
5

---------------------------------------------------------------------------------------------------------------------------------------
通过题意我们很容易发现这是一个最小割问题,为什么这么说呢?对于骑士我们有两种状态,<mark>1.放在棋盘中、2.不放在棋盘中</mark>那么我们先来建一个二分图,对于权值我们赋予1,(这样好计算总共有多少骑士被放于棋盘中),那么我们假设把所有的骑士都连接在一起并且放于棋盘中,那么这样的话骑士肯定会互相攻击,也就违背了题意,那么我们肯定是要断开一些连接,也就是取走一些骑士,那么断掉哪些边呢,肯定是越少越好,诶~~~~~~~这是啥???最小割,最小割也就是最大流,那么我们就建图跑最大流好了,<mark>注意一个坑点就是骑士可以走八个方向</mark> 看到这如果你觉得很难理解的话那么你可以去看一下洛谷的P2774方格取数问题,这两种题型是一样的但是方格取数会更简单一些,方格取数问题,看下面的讲解
---------------------------------------------------------------------------------------------------------------------------------------

AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<string>

using namespace std;

const int MAXN = 3e6 + 5;
const int INF = 0x3f3f3f3f;
int fx[8]={1,1,-1,-1,2,2,-2,-2},fy[8]={2,-2,2,-2,1,-1,1,-1};

struct Edge
{
    int to, w, next;
}edg[MAXN];

int head[MAXN], visit[1000][1000], dep[MAXN];
int cnt = 0;

void init()
{
    memset(head, -1, sizeof(head));
    cnt = 0;
}

void Add_edge(int u, int v, int w)
{
    edg[cnt].to = v;
    edg[cnt].w = w;
    edg[cnt].next = head[u];
    head[u] = cnt++;
    edg[cnt].to = u;
    edg[cnt].w = 0;
    edg[cnt].next = head[v];
    head[v] = cnt++;
}

bool BFS(int s, int t)
{
    queue<int> q;
    while(!q.empty())
    {
        q.pop();
    }
    memset(dep, 0, sizeof(dep));
    dep[s] = 1;
    //flow[s] = INF;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edg[i].next)
        {
            int v = edg[i].to;
            if(edg[i].w && dep[v] == 0)
            {
                dep[v] = dep[u] + 1;
                q.push(v);
                if(v == t)
                    return true;
            }
        }
    }
    return false;
}

int DFS(int s, int mw, int t)
{
	if(s == t)
		return mw;
	int res = 0;
	for(int i = head[s]; i != -1 && res < mw; i = edg[i].next)
	{
		int v = edg[i].to;
		if(edg[i].w && dep[v] == dep[s] + 1)
		{
			int k = DFS(v, min(mw - res, edg[i].w), t);
			if(!k) dep[v] = 0;
			edg[i].w -= k;
			edg[i^1].w += k;
			res += k;
		}
	}
	return res;
}

int Dinic(int s, int t)
{
    int tot = 0;
    while(BFS(s, t))
    {
        while(int d = DFS(s, INF, t))
        {
            tot += d;
        }
    }
    return tot;
}

int main()
{
    memset(visit, 0, sizeof visit);
    int n, m;
    scanf("%d %d", &n, &m);
    int s = 0, t = n * n + 1;
    init();
    for(int i = 1; i <= m; ++i)
    {
        int x, y;
        scanf("%d %d", &x, &y);
        visit[x][y] = 1;
    }
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= n; ++j)
        {
            if((i + j) & 1)
            {
                if(!visit[i][j])
                Add_edge(s, (i - 1) * n + j, 1);
            }
            else
            {
                if(!visit[i][j])
                Add_edge((i - 1) * n + j, t, 1);
            }
        }
    }
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= n; ++j)
        {
            if(((i + j) & 1) == 0)
                continue;
            for(int k = 0; k < 8; k++)
            {
                int x = i + fx[k];
                int y = j + fy[k];
                if(x >= 1 && x <= n && y >= 1 && y <= n && !visit[x][y])
                {
                    Add_edge((i - 1) * n + j, (x - 1) * n + y, INF);
                }

            }
        }

    }
    int ans = Dinic(s, t);
    //cout << ans << endl;
    printf("%d\n", (n * n) - m - ans);
}

题目描述
在一个有 m*n 个方格的棋盘中,每个方格中有一个正整数。现要从方格中取数,使任意 2 个数所在方格没有公共边,且取出的数的总和最大。试设计一个满足要求的取数算法。对于给定的方格棋盘,按照取数要求编程找出总和最大的数。
输入格式
第 1 行有 2 个正整数 m 和 n,分别表示棋盘的行数和列数。接下来的 m 行,每行有 n 个正整数,表示棋盘方格中的数。
输出格式
程序运行结束时,将取数的最大总和输出

输入
3 3
1 2 3
3 2 3
2 3 1
输出
11
说明/提示
m,n<=100
---------------------------------------------------------------------------------------------------------------------------------------
对于方格取数问题,其实本质上是和骑士共存问题一样的,我们同样是奇偶建二分图跑最小割,直接上代码
---------------------------------------------------------------------------------------------------------------------------------------

AC代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<queue>
#include<string>

using namespace std;

const int MAXN = 1e5 + 5;
const int INF = 0x3f3f3f3f;
const int fx[4]={0,0,1,-1};
const int fy[4]={1,-1,0,0};

struct Edge
{
    int to, w, next;
}edg[MAXN];

int head[MAXN], flow[MAXN], dep[MAXN];
int cnt = 0;

void init()
{
    memset(head, -1, sizeof(head));
    cnt = 0;
}

void Add_edge(int u, int v, int w)
{
    edg[cnt].to = v;
    edg[cnt].w = w;
    edg[cnt].next = head[u];
    head[u] = cnt++;
    edg[cnt].to = u;
    edg[cnt].w = 0;
    edg[cnt].next = head[v];
    head[v] = cnt++;
}

bool BFS(int s, int t)
{
    queue<int> q;
    while(!q.empty())
    {
        q.pop();
    }
    memset(dep, 0, sizeof(dep));
    dep[s] = 1;
    flow[s] = INF;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front(); q.pop();
        for(int i = head[u]; i != -1; i = edg[i].next)
        {
            int v = edg[i].to;
            if(edg[i].w && dep[v] == 0)
            {
                dep[v] = dep[u] + 1;
                q.push(v);
                if(v == t)
                    return true;
            }
        }
    }
    return false;
}

int DFS(int s, int mw, int t)
{
	if(s == t)
		return mw;
	int res = 0;
	for(int i = head[s]; i != -1 && res < mw; i = edg[i].next)
	{
		int v = edg[i].to;
		if(edg[i].w && dep[v] == dep[s] + 1)
		{
			int k = DFS(v, min(mw - res, edg[i].w), t);
			if(!k) dep[v] = 0;
			edg[i].w -= k;
			edg[i^1].w += k;
			res += k;
		}
	}
	return res;
}

int main()
{
    int n, m, x, sum = 0;
    scanf("%d %d", &n, &m);
    init();
    int s = 0, t = n * m + 1;
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= m; ++j)
        {
            scanf("%d", &x);
            sum += x;
            if((i +j) % 2 == 1)
            {
                Add_edge(s, (i - 1) * m + j, x);
            }
            else
                Add_edge((i - 1) * m + j, t, x);
        }
    }
    for(int i = 1; i <= n; ++i)
    {
        for(int j = 1; j <= m; ++j)
        {
            if((i + j) % 2 == 1)
            {
                for(int k = 0; k < 4; ++k)
                {
                    int x = i + fx[k];
                    int y = j + fy[k];
                    if(x > n || x <= 0 || y > m || y <= 0)
                        continue;
                    Add_edge((i - 1) * m + j, (x - 1) * m + y, INF);
                }
            }

        }
    }
    while(BFS(s, t))
        sum -= DFS(s, INF, t);
    printf("%d\n", sum);
}