You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

4.4 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

AcWing 376. 机器任务

一、题目描述

有两台机器 AB 以及 K 个任务。

机器 AN 种不同的模式(模式 0N1),机器 BM 种不同的模式(模式 0M1)。

两台机器最开始都处于模式 0

每个任务既可以在 A 上执行,也可以在 B 上执行。

对于每个任务 i,给定两个整数 a[i]b[i],表示如果该任务在 A 上执行,需要设置模式为 a[i],如果在 B 上执行,需要模式为 b[i]

任务可以以任意顺序被执行,但每台机器转换一次模式就要重启一次。

求怎样分配任务并合理安排顺序,能使机器重启次数最少。

输入格式 输入包含多组测试数据。

每组数据第一行包含三个整数 N,M,K

接下来 K 行,每行三个整数 i,a[i]b[i]i 为任务编号,从 0 开始。

当输入一行为 0 时,表示输入终止。

输出格式 每组数据输出一个整数,表示所需的机器最少重启次数,每个结果占一行。

数据范围 N,M<100,K<1000 0≤a[i]<N 0≤b[i]<M

输入样例

5 5 10
0 1 1
1 1 2
2 1 3
3 1 4
4 2 1
5 2 2
6 2 3
7 2 4
8 3 3
9 4 3
0

输出样例:

3

二、题目解析

解读:这题啥意思呢?没看懂,没办法,研究一下测试用例,瞬时明白: A机开1,2模式,B机开3模式,就可以处理完所有任务!!

A机需要0->1->2共两次,B机需要0->3共一次,合计3次!

总结

  • 看不懂题意需要结合测试用例理解
  • 0模式比较特殊,因为它本身出发就在0模式,不用切换,如果给出的数据为模式0的可以不用考虑,不会对结果产生影响

理论知识

最小点覆盖」:给出一个图,从中选出最少的点,使得每一条边的两个点里面至少有一个点是被选出来的。

在「二分图中」,最小点覆盖有一个结论:「最小点覆盖数量==最大匹配数量」。

回到本题

首先,题目中给出两个机器都从模式0开始,并且任务不必按照顺序执行,所以我们可以先把所有使用模式0执行的任务先全部完成(避免切换模式),所以剩下的任务都是大于0的。

因此问题就变成了在N+M-2(其中NM是两种机器的模式数量)种模式中,最少选出多少个模式可以把所有剩余的任务完成。

如果我们「把a[i]b[i]看成a[i]连向b[i]的一条有向边」,那么「完成任务i」就相当于是「在这条边上选择出一个点」。 如此,我们就把问题转换成了,在这个「二分图中」选择出最少的点,把所有的边覆盖到,也就是「最小点覆盖问题」,那么「二分图中的最小点覆盖问题 就等价于 最大匹配数量」,因此我们直接「使用匈牙利算法求出最大匹配数量」就是本题的答案。

三、实现代码

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

const int N = 2 * 110, M = 1010;

int n, m, k;

// 链式前向星
int e[M], h[N], idx, w[M], ne[M];
void add(int a, int b, int c = 0) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

// 匈牙利算法模板
int match[N], st[N];
int find(int u) {
    for (int i = h[u]; ~i; i = ne[i]) {
        int v = e[i];
        if (!st[v]) {
            st[v] = 1;
            if (!match[v] || find(match[v])) {
                match[v] = u;
                return 1;
            }
        }
    }
    return 0;
}

int main() {
    while (scanf("%d", &n), n) {
        scanf("%d %d", &m, &k);
        memset(match, 0, sizeof match);
        memset(h, -1, sizeof h);
        idx = 0;
        while (k--) {
            int t, a, b;
            scanf("%d %d %d", &t, &a, &b);
            if (!a || !b) continue; // 0状态不用处理
            add(a, b);              // 此边是一个任务
        }

        int res = 0;
        for (int i = 1; i < n; i++) {
            memset(st, 0, sizeof st);
            if (find(i)) res++;
        }
        printf("%d\n", res);
    }
    return 0;
}