欢迎大家访问博客:博客传送门
机器任务
题目描述
核心思路
这一题其实是二分图最大匹配的应用。
结论:二分图的最小顶点覆盖=二分图的最大匹配
最小顶点覆盖:用最少的顶点来覆盖图中所有的边
二分图最小顶点覆盖的模型特点是:每条边有2个端点,二者至少选择一个。我们称之为“2要素”。如果一个题目具有“2要素”的特点,那么就可以尝试抽象成二分图最小顶点覆盖模型来求解
在这道题目中,每个任务要么在机器 A A A上以模式 a [ i ] a[i] a[i]执行,要么在机器 B B B上以模式 b [ i ] b[i] b[i]执行,二者必选其一。因此,我们可以把机器 A A A的 N N N种模式作为 N N N个顶点放在左部点集合中,把机器 B B B的 M M M种模式作为 M M M个顶点放在右部点集合中,每个任务当作是一条无向边,连接左边的 a [ i ] a[i] a[i]模式(第 a [ i ] a[i] a[i]个节点)和右边的 b [ i ] b[i] b[i]模式(第 b [ i ] b[i] b[i]个节点)。
由于每台机器转换一次模式就需要重启一次,要想使得重启次数最少,那么就需要某种模式它能处理多种任务,即一个节点能够连接多个无向边。那么要使重启次数最少,即用最少的顶点来覆盖掉所有的无向边。那么这就是要求二分图的最小顶点覆盖,根据前面的结论可知,只需要求出二分图的最大匹配即可。
这里有个坑点,两台机器一开始的模式都是0,因此每个任务如果可以在模式为0的情况下都可以直接进行,则不需要进行重启,因此建图时碰到 a [ i ] a[i] a[i]或者 b [ i ] b[i] b[i]为0,则直接跳过不考虑把它建立到图中即可。
代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=110;
int n,m,k;
int match[N];
int g[N][N];
bool st[N];
bool find(int x)
{
//遍历机器B中的这m个模式 即寻找右边的女生集合
for(int i=0;i<m;i++)
{
//如果x这个男生还没有访问过i这个女生
//并且 此时并不是处于模式0
if(!st[i]&&g[x][i]==1)
{
st[i]=true;
if(match[i]==-1||find(match[i]))
{
match[i]=x;
return true;
}
}
}
return false;
}
int main()
{
while(scanf("%d",&n),n)
{
//有多组测试数据 因此每次都要进行初始化
memset(match,-1,sizeof match);
memset(g,0,sizeof g);
scanf("%d%d",&m,&k);
while(k--)
{
int id,a,b;
scanf("%d%d%d",&id,&a,&b);
//如果机器A处于模式0或者机器B处于模式0 那么g[a][b]=0;否则g[a][b]=1
if(a&&b)
g[a][b]=1;
}
int res=0;
//机器A的n个模式看成是左边的男生集合 从男生集合出发进行匹配
for(int i=0;i<n;i++)
{
memset(st,0,sizeof st);
if(find(i))
res++;
}
printf("%d\n",res);
}
return 0;
}