376. 机器任务

题目链接

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
0a[i]<N
0b[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

解题思路

二分图最小点覆盖

点覆盖即选择若干点使得任意的边都至少有一个被选择的点,最小点覆盖即选择的点数最少
对于二分图,有结论:=
证明:不难证明对于最大匹配,可以在匹配边的任意一端选择一个点,即 ,不妨构造证明 =,一开始分为左右两部,对于左部的所有非匹配点都开始做一遍增广(,,,),但由于是最大匹配,即最后不可能走到非匹配点,标记所有遍历到的点,对此不难发现如下性质:

  1. 左边所有非匹配点都被标记
  2. 右边所有非匹配点都没有被标记(否则说明找到了增广路)
  3. 对于匹配边而言,两边要么同时被标记要么同时未被标记(由增广路径很容易得到)

开始构造:对于每个匹配边,如果左右两边都被标记的话则选择右边,否则,即两边同时未被标记,此时选择左边

显然,不可能存在非匹配点和非匹配点连边,否则说明找到一条增广路径,与最大匹配矛盾,故只需要讨论 的边以及 的边,对于 ,由于左部的非匹配点都被标记,按照构造方法,此时该 也应该被标记,由性质 3,这时该匹配点是被选择的,所以这样的边能够被覆盖到;对于 ,如果左部的 被标记的话,此时该 会被标记,这与性质 2 矛盾,故此时 未被标记,即此时该 也是被选择,这样所有的边都能被覆盖

本题先处理两种模式为 0 的情况,因为这种模式一开始就可以处理掉,然后用任务表示边,两个端点即两种模式,即任意一种模式都可以消除这条边,正好对应二分图的最小点覆盖

  • 时间复杂度:O(nm)

代码

// Problem: 机器任务 // Contest: AcWing // URL: https://www.acwing.com/problem/content/378/ // Memory Limit: 10 MB // Time Limit: 1000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=105; int n,m,k; bool g[N][N],st[N]; int match[N],color[N]; bool find(int x) { for(int i=0;i<m;i++) if(g[x][i]&&!st[i]) { st[i]=true; if(match[i]==0||find(match[i])) { match[i]=x; return true; } } return false; } int main() { while(scanf("%d",&n),n) { memset(g,0,sizeof g); memset(match,0,sizeof match); scanf("%d%d",&m,&k); while(k--) { int i,a,b; scanf("%d%d%d",&i,&a,&b); if(a==0||b==0)continue; g[a][b]=true; } int res=0; for(int i=0;i<n;i++) { memset(st,0,sizeof st); if(find(i))res++; } printf("%d\n",res); } return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16936563.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(22)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示