Acwing 376.机器任务 (最小点覆盖)

简介: 笔记

Acwing 376.机器任务


题意

有两台机器 A,B以及 K 个任务。


机器 A 有 N种不同的模式(模式0 ∼ N − 1 ),机器 B 有 M 种不同的模式(模式 0 ∼ M − 1)。


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


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


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


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


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


思路

将机器A的所有模式看作左边的集合,机器B 的所有模式看作右边的集合,模式看作顶点,任务看作连接机器 A 和机器 B 的一条边,原题可以抽象成一个二分图。那么题目要求的就是,在这张二分图中最少选多少个顶点(模式),可以覆盖所有的边(任务),即最小点覆盖问题。 而我们又知道在二分图中 最小点覆盖 == 最大匹配数,所以直接用匈牙利算法求二分图的最大匹配即可。


需要注意的是,两台机器最初都处于模式0,所以不用转换模式即可将a[i] 或b[i] 为0的任务完成,所以建图时将这种情况忽略即可。


代码

// Author:zzqwtcc
// Problem: 机器任务
// Contest: AcWing
// Time:2021-10-07 14:17:44
// URL: https://www.acwing.com/problem/content/378/
// Memory Limit: 10 MB
// Time Limit: 1000 ms
#include<bits/stdc++.h>
#include<unordered_map>
// #define int long long
#define INF 0x3f3f3f3f
#define INFL 0x3f3f3f3f3f3f3f3f
#define mod 1000000007
#define MOD 998244353
#define rep(i, st, ed) for (int (i) = (st); (i) <= (ed);++(i))
#define pre(i, ed, st) for (int (i) = (ed); (i) >= (st);--(i))
#define debug(x,y) cerr << (x) << " == " << (y) << endl;
using namespace std;
typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int, int> PII;
template<typename T> inline T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }
template<typename T> inline T lowbit(T x) { return x & -x; }
// template<typename T> T qmi(T a, T b = mod - 2, T p = mod) { T res = 1; b %= (p - 1 == 0 ? p : p - 1); while (b) { if (b & 1) { res = (LL)res * a % p; }b >>= 1; a = (LL)a * a % p; }return res % mod; }
const int N = 110;
int n,m,k;
vector<int>vec[N];
bool st[N];
int match[N];
bool find(int u){
  for(int i =0 ; i < vec[u].size();++i){
    int j =vec[u][i];
    if(st[j])continue;
    st[j] = true;
    if(match[j] == -1 || find(match[j])){
      match[j] = u;
      return true;
    }
  }
  return false;
}
void solve() {
    while(cin >> n, n){
      for(int i = 0 ;i <= 100;++i)vec[i].clear();
      memset(match,-1,sizeof match);
      cin >> m >> k;
      while(k--){
        int id,u,v;cin >> id >> u >> v;
        if(u && v)
          vec[u].push_back(v);
      }
      int res = 0;
      for(int i = 0; i <= n;++i){
        memset(st,0,sizeof st);
        if(find(i))
          res++;
      }
      for(int i = 0; i <= m;++i){
          if(match[i] == 0){
              res--;
              break;
          }
      }
      cout << res << endl;
    }
}
signed main() {
    // int _; cin >> _;
    // while (_--)
        solve();
    return 0;
}


目录
相关文章
|
5月前
|
算法
力扣经典150题第十五题:分发糖果
力扣经典150题第十五题:分发糖果
34 0
|
5月前
|
机器学习/深度学习 存储
【洛谷 P1028】[NOIP2001 普及组] 数的计算 题解(递归)
**NOIP2001普及组数的计算**:给定自然数\( n \),构造数列,新数不超过序列最后一项一半。求合法数列个数。输入\( n \)(\(1 \leq n \leq 10^3\))。样例:输入6,输出6。递归解决,代码中定义函数`f`实现递归计算,总和存储在`cnt`中,最后输出。
50 0
|
5月前
|
机器学习/深度学习 人工智能
【洛谷 P1028】[NOIP2001 普及组] 数的计算 题解(递推)
在NOIP2001普及组的数的计算题目中,给定自然数`n`,需构造遵循特定规则的合法数列。合法序列始于`n`,新元素不超过前一项的一半。任务是找出所有这样的数列数量。例如,当`n=6`时,合法序列包括`6`, `6,1`, `6,2`, `6,3`, `6,2,1`, `6,3,1`。程序通过动态规划求解,当`i`为奇数时,`a[i] = a[i - 1]`;为偶数时,`a[i] = a[i - 1] + a[i / 2]`。代码中预处理数组`a`并输出`a[n]`作为答案。输入`n`后,程序直接计算并打印合法数列个数。
56 0
|
6月前
|
索引
[经典力扣面试题]135. 分发糖果
[经典力扣面试题]135. 分发糖果
|
机器学习/深度学习 存储 人工智能
第1章:算法基础【AcWing】
第1章:算法基础【AcWing】
172 0
|
算法 C语言 C++
LeetCode.每日一题 2427. 公因子的数目
将一个数的所有约数枚举出来,存入数组,之后再用数组中的每一个数,去看看能不能被第二个数整除,若能则答案++
58 0
《蓝桥杯每日一题》dfs·AcWing3502. 不同路径数
《蓝桥杯每日一题》dfs·AcWing3502. 不同路径数
66 0
|
算法 C++
【每日算法Day 69】面试经典题:分发糖果问题
【每日算法Day 69】面试经典题:分发糖果问题
100 0
|
人工智能 算法
AcWing算法学习第三节---高精度问题.
AcWing算法学习第三节---高精度问题.
|
算法
【算法作业】实验一:轮流报数与鸡兔同笼
【算法作业】实验一:轮流报数与鸡兔同笼
154 0
【算法作业】实验一:轮流报数与鸡兔同笼