【洛谷】4304:[TJOI2013]攻击装置【最大点独立集】【二分图】2172: [国家集训队]部落战争【二分图/网络流】【最小路径覆盖】

P4304 [TJOI2013]攻击装置

题目描述

给定一个01矩阵,其中你可以在0的位置放置攻击装置。 每一个攻击装置(x,y)都可以按照“日”字攻击其周围的8个位置(x-1,y-2),(x-2,y-1),(x+1,y-2),(x+2,y-1),(x-1,y+2),(x-2,y+1),(x+1,y+2),(x+2,y+1)

求在装置互不攻击的情况下,最多可以放置多少个装置。

输入输出格式

输入格式:

 

第一行一个整数N,表示矩阵大小为N*N。

接下来N行每一行一个长度N的01串,表示矩阵。

 

输出格式:

 

一个整数,表示在装置互不攻击的情况下最多可以放置多少个装置。

 

输入输出样例

输入样例#1: 复制
3
010
000
100
输出样例#1: 复制
4

说明

30%数据N<=50

100%数据 N<=200


Solution

求最大点独立集,最大点独立集=点集-最大匹配

这道题因为是会相互连边,所以最后最大匹配数除以2就行了。

Code

#include<bits/stdc++.h>
using namespace std;

struct Node {
    int u, v, nex;
    Node(int u = 0, int v = 0, int nex = 0) :
        u(u), v(v), nex(nex) { }
} Edge[4000005];

int h[100005], stot;
void add(int u, int v) {
    Edge[++stot] = Node(u, v, h[u]);
    h[u] = stot;
}

int n;
char s[205][205];

int zl[9][2] = {{-1, -2}, {-2, -1}, {2, 1}, {1, 2}, {1, -2}, {-2, 1}, {-1, 2}, {2, -1}};
int vis[100005], las[100005], to[100005], G[100005], id[205][205], t;

bool dfs(int u) {
    for(int i = h[u]; i; i = Edge[i].nex) {
        int v = Edge[i].v;
        if(vis[v] != t) {
            vis[v] = t;
            if(!las[v] || dfs(las[v])) {
                las[v] = u; to[u] = v;
                return 1;
            }
        }
    }
    return 0;
}

bool pd(int x, int y) {
    if(x < 0 || y < 0 || x > n || y > n || !G[id[x][y]])    return 0;
    return 1;
}

int main() {
    int tot = 0, ans = 0;
    scanf("%d", &n);
    for(int i = 1; i <= n; i ++)    scanf("%s", s[i] + 1);
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= n; j ++) {
            id[i][j] = (i - 1) * n + j;
            if(s[i][j] == '0')    G[id[i][j]] = 1, tot ++;
            else                G[id[i][j]] = 0;
        }
    for(int i = 1; i <= n; i ++)
        for(int j = 1; j <= n; j ++) {
            if(!G[id[i][j]])    continue;
            for(int k = 0; k < 8; k ++) {
                int x = i + zl[k][0], y = j + zl[k][1];
                if(!pd(x, y))    continue;
                add(id[i][j], id[x][y] + n * n);
            }
        }
    for(int i = 1; i <= n * n; i ++)
        if(!to[i] && G[i]) {
            t ++;
            ans += dfs(i);
        }
    printf("%d", tot - ans / 2);
    return 0;
}

 


P2172 [国家集训队]部落战争

题目描述

lanzerb的部落在A国的上部,他们不满天寒地冻的环境,于是准备向A国的下部征战来获得更大的领土。

A国是一个M*N的矩阵,其中某些地方是城镇,某些地方是高山深涧无人居住。lanzerb把自己的部落分成若干支军队,他们约定:

  1. 每支军队可以从任意一个城镇出发,并只能从上往向下征战,不能回头。途中只能经过城镇,不能经过高山深涧。

  2. 如果某个城镇被某支军队到过,则其他军队不能再去那个城镇了。

  3. 每支军队都可以在任意一个城镇停止征战。

  4. 所有军队都很奇怪,他们走的方法有点像国际象棋中的马。不过马每次只能走1*2的路线,而他们只能走R*C的路线。

lanzerb的野心使得他的目标是统一全国,但是兵力的限制使得他们在配备人手时力不从心。假设他们每支军队都能顺利占领这支军队经过的所有城镇,请你帮lanzerb算算至少要多少支军队才能完成统一全国的大业。

输入输出格式

输入格式:

 

第一行包含4个整数M、N、R、C,意义见问题描述。接下来M行每行一个长度为N的字符串。如果某个字符是'.',表示这个地方是城镇;如果这个字符时'x',表示这个地方是高山深涧。

 

输出格式:

 

输出一个整数,表示最少的军队个数。

 

输入输出样例

输入样例#1: 复制
3 3 1 2
...
.x.
...
输出样例#1: 复制
4
输入样例#2: 复制
5 4 1 1
....
..x.
...x
....
x...
样例输出
输出样例#2: 复制
5

说明

100%的数据中,1<=M,N<=50,1<=R,C<=10。


Solution

比较轻松地可以想到最小路径覆盖问题,用最少的路径,经过所有点。

二分图是经典方法,每个点拆点,两点间有连边就在二分图上连边,最小路径=总点数-最大匹配。可以理解为,每一个匹配,就省去了一条路径(以入点为起点的路径可以由出点直接到达),所以最大匹配省去的路径是最多的。

最大匹配也可以用网络流实现。

注意空间....

Code

#include<bits/stdc++.h>
using namespace std;

int G[105][105], n, m, r, c;
int zl[2][2] = {{1, 1}, {1, -1}};

struct Node {
    int u, v, nex;
    Node(int u = 0, int v = 0, int nex = 0) :
        u(u), v(v), nex(nex) { }
} Edge[1000005];

int h[10005], stot;
void add(int u, int v) {
    Edge[++stot] = Node(u, v, h[u]);
    h[u] = stot;
}

bool check(int x, int y) {
    if(x < 1 || y < 1 || x > m || y > n || !G[x][y])    return 0;
    return 1;
}

int las[10005], to[10005], vis[10005];
bool dfs(int u) {
    for(int i = h[u]; i; i = Edge[i].nex) {
        int v = Edge[i].v;
        if(!vis[v]) {
            vis[v] = 1;
            if(!las[v] || dfs(las[v])) {
                to[u] = v; las[v] = u;
                return 1;
            }
        }
    }
    return 0;
}

int id[105][105], ans, tot;
char s[505][505];
int main() {
    scanf("%d%d%d%d\n", &m, &n, &r, &c);
    for(int i = 1; i <= m; i ++)    scanf("%s", s[i] + 1);
    for(int i = 1; i <= m; i ++) {
        for(int j = 1; j <= n; j ++) {
            id[i][j] = (i - 1) * n + j;
            if(s[i][j] == '.')    G[i][j] = 1, tot ++;
            else            G[i][j] = 0;
        }
    }
    for(int i = 1; i <= m; i ++)
        for(int j = 1; j <= n; j ++) {
            if(!G[i][j])    continue;
            int x, y;
            for(int k = 0; k < 2; k ++) {
                x = i + zl[k][0] * r, y = j + zl[k][1] * c;
                if(!check(x, y))    continue;
                add(id[i][j], id[x][y] + n * m);
            }
            if(r != c) {
                for(int k = 0; k < 2; k ++) {
                    x = i + zl[k][0] * c, y = j + zl[k][1] * r;
                    if(!check(x, y))    continue;
                    add(id[i][j], id[x][y] + n * m);
                }
            }
        }
    for(int i = 1; i <= m; i ++) {
        for(int j = 1; j <= n; j ++)
            if(!to[id[i][j]] && G[i][j]) {
                memset(vis, 0, sizeof(vis));
                ans += dfs(id[i][j]);
            }
    }
    printf("%d", tot - ans);
    return 0;
}
posted @ 2018-10-16 16:18  Wans_ovo  阅读(234)  评论(0编辑  收藏  举报