问题描述
众所周知,机器调度是计算机科学中一个非常经典的问题,研究历史非常悠久。调度问题在必须满足的约束的性质和所需的调度类型方面存在很大差异。这里我们考虑一个 2 机调度问题。
有两台机器A和B。机器A有n种工作模式,称为mode_0,mode_1,…,mode_n-1,同样机器B有m种工作模式,mode_0,mode_1,…,mode_m-1。一开始他们都在mode_0工作。
对于给定的 k 个作业,它们中的每一个都可以在两台机器中的任何一台上以特定模式处理。例如,作业 0 可以在机器 A 的 mode_3 或机器 B 的 mode_4 处理,作业 1 可以在机器 A 的 mode_2 或机器 B 的 mode_4 处理,依此类推。因此,对于作业 i,约束可以表示为三元组 (i, x, y),这意味着它可以在机器 A 的 mode_x 或机器 B 的 mode_y 中处理。
显然,要完成所有的工作,我们需要不时改变机器的工作模式,但不幸的是,机器的工作模式只能通过手动重启来改变。通过更改作业的顺序并将每个作业分配给合适的机器,请编写程序以最大限度地减少重新启动机器的时间。
输入
该程序的输入文件包含几个配置。一个配置的第一行包含三个正整数:n、m (n, m < 100) 和 k (k < 1000)。下面的 k 行给出了 k 个作业的约束,每行是一个三元组:i, x, y。
输入将由包含单个零的行终止。
输出
输出应该是每行一个整数,这意味着重新启动机器的最少次数。
样本输入
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, 右侧为B,机器初始状态为A0,B0。以样例进行说明
A0转换到A1,启动一次,进行4个作业
A1转换到A2,又启动一次,进行4个作业
B0转换到B3,又启动一次,进行2个作业
所有作业完成,共启动3次。即
求最小顶点覆盖数 = 匹配数
#include<bits/stdc++.h>
using namespace std;
int n, m, k, id, x, y, ans;
int g[105][105]; // 二维数组
int vis[105]; // 标记右边是否已匹配
int linker[105]; // 下标为右边机器编号,存放值为左边匹配的编号
bool dfs(int u) { // 搜索左边每个节点的匹配对象
for(int i = 1; i <= m; i++) {
if(g[u][i] && !vis[i]) { // 是否能够匹配,是否被访问过
vis[i] = 1; // 这里注意需要标记访问,不然进行下面搜索的话会死循环
// 右节点未被匹配, 匹配了则搜索匹配的左边节点的新的可以匹配的对象,
//如左2可以匹配右1,将右1之前的匹配对象左1,重新寻找可匹配的对象,找到右二,
//这个过程中,之前的vis标记就起作用了,不会让左1又找到右1
if(!linker[i] || dfs(linker[i])) {
linker[i] = u;
return true;
}
}
}
return false; // 注意这里返回false,即找遍了右节点,没有一个可以和左节点匹配
}
int hungary() {
ans = 0; // 计算匹配数
memset(linker, 0, sizeof(linker)); // 注意这个初始化位置
for(int i = 1; i <= n; i++) {
memset(vis, 0, sizeof(vis)); // 注意这个初始化位置
if(dfs(i)) ans++;
}
return ans;
}
int main() {
while(scanf("%d", &n), n) {
memset(g, 0, sizeof(g));
scanf("%d %d", &m, &k);
for(int i = 0; i < k; i++) {
scanf("%d %d %d", &id, &x, &y);
if(x == 0 || y == 0) continue; // 如果可以在A0或者B0机器上进行的话,是不需要重启的,所以可以忽略
g[x][y] = 1;
}
printf("%d\n", hungary());
}
return 0;
}