P3355 骑士共存问题【最大独立点集】

在一个 n*n个方格的国际象棋棋盘上,马(骑士)可以攻击的棋盘方格如图所示。棋盘上某些方格设置了障碍,骑士不得进入
这里写图片描述
对于给定的 n*n 个方格的国际象棋棋盘和障碍标志,计算棋盘上最多可以放置多少个骑士,使得它们彼此互不攻击
输入格式:
第一行有 2 个正整数n 和 m (1<=n<=200, 0<=m< n*n),分别表示棋盘的大小和障碍数。接下来的 m 行给出障碍的位置。每行 2 个正整数,表示障碍的方格坐标。
输出格式:
将计算出的共存骑士数输出
输入样例#1:
3 2
1 1
3 3
输出样例#1:
5

分析:类似于方格取数问题
这里没有权值,等价于你挑出一些点,这些点互相之间没有边,最大独立点集问题(=顶点数-最小顶点覆盖,最小顶点覆盖=最大匹配)。
分两个集合:是二分图集合,集合内部没有边,集合之间建边(如题意所示图)。最大独立点集问题=顶点数-最小顶点覆盖,最小顶点覆盖=最大匹配,常规建图,问题可解(八个方向我用了一个技巧建图)。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
typedef long long LL;
using namespace std;

const int MAXN = 80000 + 50;
const int INF = 0x3f3f3f3f;
int head[MAXN], dist[MAXN], vis[MAXN];
int cur[MAXN], ma[MAXN], arr[210][210];
int n, m;
int top = 0;

struct Edge {
    int to, cap, flow, next;
}edge[MAXN * 20];

void init() {
    top = 0;
    memset(head, -1, sizeof(head));
    memset(vis, 0, sizeof(vis));
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= n; ++j) {
            arr[i + 3][j + 3] = 1;
        }
    }
}

void addedge(int a, int b, int c) {
    Edge E1 = {b, c, 0, head[a]};
    edge[top] = E1;
    head[a] = top++;
    Edge E2 = {a, 0, 0, head[b]};
    edge[top] = E2;
    head[b] = top++;
}

bool BFS(int st, int ed) {
    memset(dist, -1, sizeof(dist));
    memset(vis, 0, sizeof(vis));
    queue<int> que;
    que.push(st);
    vis[st] = 1;
    dist[st] = 0;
    while(!que.empty()) {
        int u = que.front();
        que.pop();
        for(int i = head[u]; i != -1; i = edge[i].next) {
            Edge E = edge[i];
            if(!vis[E.to] && E.cap > E.flow) {
                dist[E.to] = dist[u] + 1;
                vis[E.to] = 1;
                if(E.to == ed) return true;
                que.push(E.to);
            }
        }
    }
    return false;
}

int DFS(int x, int a, int ed) {
    if(x == ed || a == 0) return a;
    int flow = 0, f;
    for(int& i = cur[x]; i != -1; i = edge[i].next) {
        Edge& E = edge[i];
        if(dist[E.to] == dist[x] + 1 && (f = DFS(E.to, min(a, E.cap - E.flow), ed)) > 0) {
            E.flow += f;
            edge[i^1].flow -= f;
            flow += f;
            a -= f;
            if(a == 0) break;
        }
    }
    return flow;
}

int Maxflow(int st, int ed) {
    int flow = 0;
    while(BFS(st, ed)) {
        memcpy(cur, head, sizeof(head));
        flow += DFS(st, INF, ed);
    }
    return flow;
}

int main()
{
    scanf("%d %d", &n, &m);
    init();
    for(int i = 1; i <= m; ++i) {
        int a, b;
        scanf("%d %d", &a, &b);
        ma[a * (n - 1) + b] = 1;
        arr[a + 3][b + 3] = 0;//八个方向稍微麻烦点,我用了一个技巧,就不用考虑数组越界了
    }
    int sum = 0, T = n * n + 1;
    for(int i = 1; i <= n; ++i) {
        for(int j = 1; j <= n; ++j) {
            int ans = 0;
            if(!arr[i + 3][j + 3]) continue;
            if((i + j) % 2 == 0) addedge(0, n * (i - 1) + j, 1);
            else addedge(n * (i - 1) + j, T, 1);
            if((i + j) % 2) continue;
            if(arr[i + 1 + 3][j + 2 + 3]) addedge(n * (i - 1) + j, n * (i - 1) + j + n + 2, INF);
            if(arr[i + 2 + 3][j + 1 + 3]) addedge(n * (i - 1) + j, n * (i - 1) + j + n * 2 + 1, INF);
            if(arr[i + 2 + 3][j - 1 + 3]) addedge(n * (i - 1) + j, n * (i - 1) + j + n * 2 - 1, INF);
            if(arr[i + 1 + 3][j - 2 + 3]) addedge(n * (i - 1) + j, n * (i - 1) + j + n - 2, INF);
            if(arr[i - 1 + 3][j - 2 + 3]) addedge(n * (i - 1) + j, n * (i - 1) + j - n - 2, INF);
            if(arr[i - 1 + 3][j + 2 + 3]) addedge(n * (i - 1) + j, n * (i - 1) + j - n + 2, INF);
            if(arr[i - 2 + 3][j - 1 + 3]) addedge(n * (i - 1) + j, n * (i - 1) + j - n * 2 - 1, INF);
            if(arr[i - 2 + 3][j + 1 + 3]) addedge(n * (i - 1) + j, n * (i - 1) + j - n * 2 + 1, INF);
        }
    }
    int cnt = Maxflow(0, n * n + 1);
    printf("%d\n", n * n - m - cnt);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36368339/article/details/80627602
今日推荐