专题十 匹配问题

文章目录专题十 匹配问题HDU - 1045 Fire NetHDU 2444 The Accomodation of StudentsHDU 1083 CoursesHDU - 1281 棋盘游戏HDU-2819 SwapHDU-2389 Rain on your ParadeHDU - 4185 Oil SkimmingPOJ - 3020 Antenna PlacementHDU - 1054 Strategic GameHDU - 1151 Air RaidPOJ - 2594 Treasure E
摘要由CSDN通过智能技术生成

专题十 匹配问题

HDU - 1045 Fire Net

题意: 给一个最多4*4的网格图,其中有些地方障碍,现在在无障碍的格子放棋子,各个棋子不能同行同列,问最多可以放多少个棋上去。

题解: 将每一行借助障碍点划分为若干个分段,每个分段缩为一个点。对行做这个操作,得到n1个行的点;对列做这个操作,得到n2个列的点。如果列的点和行的点有交叉,那么就把行的点向列的点连一条边。然后问题就变为了最大匹配问题,跑匈牙利算法即可。

微信截图_20201214233232.png

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
int const N = 10;
int n, m, T, n1, n2, belong[N][N], g[20 * 2][20 * 2], st[20], match[20];
string s[N];

bool find(int x) {
   
    for (int i = 1; i <= n2; ++i) {
   
        if (!g[x][i]) continue;
        if (!st[i]) {
   
            st[i] = 1;
            if (!match[i] || find(match[i])) {
   
                match[i] = x;
                return true;
            }
        }
    }
    return false;
}

int main() {
   
    while(scanf("%d", &n)!= EOF && n) {
   
        n1 = 0, n2 = 0;
        memset(g, 0, sizeof g);
        memset(match, 0, sizeof match);
        memset(belong, 0, sizeof belong);
        for (int i = 0; i < n; ++i) cin >> s[i];
        for (int k = 0; k < n; ++k) {
   
            for (int i = 0, j = 0; i < n; i = j) {
     // 按照障碍点分段
                j = i;
                while(j < n && s[k][i] == s[k][j]) j++;
                if (s[k][i] == 'X') continue;
                n1++;
                for (int idx = i; idx < j; ++idx) belong[k][idx] = n1;  // 将每个缩点给个标号
            }
        }
        for (int k = 0; k < n; ++k) {
   
            for (int i = 0, j = 0; i < n; i = j) {
   
                j = i;
                while(j < n && s[i][k] == s[j][k]) j++;
                if (s[i][k] == 'X') continue;
                n2++;
                for (int idx = i; idx < j; idx++) {
   
                    g[belong[idx][k]][n2] = 1;
                }
            }
        }
        int res = 0;
        for (int i = 1; i <= n1; ++i) {
   
            memset(st, 0, sizeof st);
            if (find(i)) res++;
        }
        cout << res << endl;
    }
    return 0;
}

HDU 2444 The Accomodation of Students

题意: 将所有人分成两组,每组里不能有相互认识的同学,如果不能分成这样的组输出No,

如果能,则朋友之间相互两两配对,输出最大可配对数;

题解: 模板题

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
int const MAXN = 200 + 10, MAXM = MAXN * MAXN * 2;
int n, m, T;
int e[MAXM], ne[MAXM], idx, h[MAXN], color[MAXN], st[MAXN], match[MAXN];

void add(int a, int b) {
   
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

int dfs(int u, int c) {
   
    color[u] = c;  // 给u号点打上c的颜色
    for (int i = h[u]; i != -1; i = ne[i]) {
     // 遍历所有与i号点相连的点
        int j = e[i];  // 看j号点
        if (!color[j]) {
     // 如果j号点没有染色
            if (!dfs(j, 3 - c)) return 0;  // 如果j号点染色3-c过程中失败
        }
        if (color[j] == c) return 0;  // 如果j号点也染色c颜色
    }
    return 1;  // 如果u号点的所有邻点都没有染色失败
}

bool find(int x) {
   
    for (int i = h[x]; i != -1; i = ne[i]) {
     // 遍历所有与x之间相连的点
        int j = e[i];  // 点为j
        if (!st[j]) {
     // 如果j没有匹配过
            st[j] = 1;  // 记录j匹配过
            if (!match[j] || find(match[j])) {
     // 如果右半部分的j没有匹配到左半部分的人或者j匹配到的可以去匹配其他右半部分的人
                match[j] = x;  // 记录右半部分的j和左半部分的x匹配
                return true;  // 找到x的匹配对象
            }
        }
    }
    return false;  // 全部都遍历仍然没有成功,返回false
}

int main() {
   
    while(scanf("%d%d", &n, &m) != EOF) {
   
        memset(h, -1, sizeof h);
        memset(match, 0, sizeof match);
        memset(color, 0, sizeof color);
        idx = 0;
        for (int i = 1, a, b; i <= m; ++i) {
   
            scanf("%d%d", &a, &b);
            add(a, b), add(b, a);  // 可以建2条边,最后答案除以2即可
        }
        int flg = 1;  // flg记录染色是否成功,成功为1,失败为0
        for (int i = 1; i <= n; ++i) {
     // 从1号点开始枚举
            if (!color[i]) {
     // 如果i号点为染色
                if (!dfs(i, 1)) {
     // 如果i号点染色失败
                    flg = 0;  // flg 打上失败标记
                    break;
                }
            }
        }
        if (!flg) {
   
            puts("No");
            continue;
        }
        int res = 0;
        for (int i = 1; i <= n; ++i) {
     // 每个点都搜索一次增广路,然后答案除以2即可
            memset(st, 0, sizeof st);
            if (find(i)) res++;
        }
        printf("%d\n", res / 2);
    }
    return 0;
}

HDU 1083 Courses

题意: 求最大匹配

题解: 水题

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
int const MAXN = 1000, MAXM = 3e5 + 10;
int n, m, T;

int e[MAXM], ne[MAXM], idx, h[MAXN], st[MAXN], match[MAXN];

void add(int a, int b) {
   
    e[idx] = b, ne[idx] = h[a], h[a] = idx++;
}

bool find(int x) {
   
    for (int i = h[x]; i != -1; i = ne[i]) {
     // 遍历所有与x之间相连的点
        int j = e[i];  // 点为j
        if (!st[j]) {
     // 如果j没有匹配过
            st[j] = 1;  // 记录j匹配过
            if (!match[j] || find(match[j])) {
     // 如果右半部分的j没有匹配到左半部分的人或者j匹配到的可以去匹配其他右半部分的人
                match[j] = x;  // 记录右半部分的j和左半部分的x匹配
                return true;  // 找到x的匹配对象
            }
        }
    }
    return false;  // 全部都遍历仍然没有成功,返回false
}

int main() {
   
    cin >> T;
    while(T--) {
   
        memset(match, 0, sizeof match);
        memset(h, -1, sizeof h);
        idx = 0;
        scanf("%d%d", &m, &n);
        for (int i = 1; i <= m; ++i) {
   
            int t;
            scanf("%d", &t);
            for (int j = 1; j <= t; ++j) {
   
                int s;
                scanf("%d", &s);
                add(i, s);
            }
        }
        int res = 0;
        for (int i = 1; i <= m; ++i) {
   
            memset(st, 0, sizeof st);
            if (find(i)) res++;
        }
        if (res == m) printf("YES\n");
        else printf("NO\n");
    }
    return 0;
}

HDU - 1281 棋盘游戏

题意: n*m的棋盘,其中只有k个位置可以放棋子。同一行或者同一列不能放“车”:
1.问最多可以放多少个棋子
2.问在第1问的基础上,有多少个棋子的位置是不能改变的,如果改变第1问的结果就会改变

Snipaste_2020-12-24_02-19-22.png

题解: 每一行每一列只能放一辆车,因此可以把行和列分别当成左部图和右部图,那么可以放的点(x, y)就可以把对应的行和列连一条边,然后跑最大匹配即可知道最多多少车。第二问问关键点个数,那么可以把每个可以放的点删去,也就是删去一条边。然后再次跑最大匹配,看最大匹配十分和第一问一样,不一样则是关键点。

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
int const MAXN = 1e2 + 10;
int n, m, T, k, kase= 1;
int g[MAXN][MAXN], st[MAXN], match[MAXN];
PII pos[MAXN * MAXN];

int find(int x) {
   
    for (int i = 1; i <= n; ++i) {
   
        if (!g[x][i]) continue;
        if (!st[i]) {
   
            st[i] = 1;
            if (!match[i] || find(match[i])) {
   
                match[i] = x;
                return true;
            }
        }
    }
    return false;
}

int hungry() {
   
    int res = 0;
    for (int i = 1; i <= n; ++i) {
   
        memset(st, 0, sizeof st);
        if (find(i)) res++;
    }
    return res;
}

int main() {
   
    while(scanf("%d%d%d", &n, &m, &k) != EOF) {
   
        for (int i = 1; i <= n; ++i) {
   
            st[i] = match[i] = 0;
        }
        memset(g, 0, sizeof g);
        for (int i = 1; i <= k; ++i) {
   
            scanf("%d%d", &pos[i].first, &pos[i].second);
            g[pos[i].first][pos[i].second] = 1;
        }
        int max_match = hungry();
        int importance = 0;
        for (int i = 1; i <= k; ++i) {
   
            g[pos[i].first][pos[i].second] = 0;
            memset(match, 0, sizeof match);
            if (hungry() != max_match) importance++;
            g[pos[i].first][pos[i].second] = 1;
        }
        printf("Board %d have %d important blanks for %d chessmen.\n", kase++, importance, max_match);
    }
    return 0;  
}

HDU-2819 Swap

题意: 交换图的某些行或者是某些列(可以都换),使得这个N*N的图对角线上全部都是1.

题解: 如果通过交换某些行没有办法的到解的话,那么只交换列 或者 既交换行又交换列 那也没办法得到解,因此只交换行或者列即可。对于 g [ i ] [ j ] = 1 g[i][j] = 1 g[i][j]=1,那么为了使得对角线变为1即 g [ i ] [ i ] = 1 g[i][i]=1 g[i][i]=1,那么交换第i行和第j列,一旦 g [ i ] [ j ] = 1 g[i][j]=1 g[i][j]=1,表明存在一条边从 i − > j i->j i>j,依次建图,跑二分匹配,如果匹配数目不为n,说明不存在交换的方式。打印路径的话,因为每个点都是一一对应的,所以找到每列要交换的列,然后swap即可。

代码:

#include <bits/stdc++.h>

using namespace std;

typedef long long LL;
typedef pair<int, int> PII;
int const N = 1e2 + 10;
int n, m, T;
int g[N][N], st[N], match[N];
int ans_l[N], ans_r[N];

bool 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值