题意:
有两台不同机器A和B,他们分别拥有各种运行模式1~n和1~m。现有一些job,需要在某模式下才能完成,job1在A和B上需要的工作模式又可能会不一样。两台机器一开始处于0模式,可以切换模式,但是仅在0模式才有切换权,可以通过重启机器回到0模式。现在有一堆job,要多少次重启机器才可以完成任务。
思路:
分析下,很明显,只要求重启次数,那么和时间无关(可以完全由1个机器来干活),只要同个模式的任务能在一块执行掉就节省重启次数了,所有模式一样的任务只耗费1次重启。但是两台机器协作可能重启次数更少,比如在1模式下,B能执行掉2个任务,但是这两个任务需要在A机器上的两种不同模式下才能执行。
我们的目的是尽量让某一个机器的一种模式能够尽可能多的完成这样的一些任务,这些任务需要在另一台机器的多种模式下完成。
可以这样建模:左边是S集{机器A的每种模式为1个点},所以有n种模式。右边是T集{机器B的每种模式为1个点},所以有m种模式。每个任务作为一条边,连接S和T上的对应模式。
按最坏情况考虑,只需要将n+m种模式都运行一次,必定可以解决所有的任务。但是为了节省重启次数,只需要挑出部分的点,能覆盖到所有的边(边即任务)。这样的模型就是二分图的最小点覆盖了。
求最小点覆盖的定义:对于图G=(V,E)中的一个包含最少的点的集合S⊆V,E中每一条边至少有一个端点在S中。
根据konig定理,最小点覆盖数=最大匹配数。那么求二分图的最大匹配就行了,可以用匈牙利算法,代码少。(匈牙利算法看“知识科普”分类)
直接变成了男女配对的模型,A的模式是都是男的,B的模式都是女的。
1 #include <bits/stdc++.h> 2 using namespace std; 3 const int N=105; 4 int n ,m, k, r, a, b; 5 6 bool mapp[N][N]; //矩阵 7 bool match[N]; //用于找路径,走过的点被标记 8 bool vis[N]; //标记女的是否已经被匹配了 9 int girl[N]; //假设B为女的 10 11 12 //假设A为男,B为女 13 int find(int x) //为x找女对象 14 { 15 for(int i=1; i<=m; i++) //扫描所有妹子 16 { 17 if( mapp[x][i] && !match[i] ) //认识的,还没有尝试过帮这个妹子另外找过对象 18 { 19 match[i]=1; //这个妹子已经试图帮她找过对象了 20 if(!vis[i] || find(girl[i])) 21 { 22 girl[i]=x; //如果能为妹子男朋友另找对象,那么这个妹子就是我的了 23 vis[i]=1; 24 return true; 25 } 26 } 27 } 28 return false; 29 } 30 31 int hungary() 32 { 33 int cnt=0; 34 for(int i=1; i<=n; i++) 35 { 36 memset(match,0,sizeof(match)); 37 if(find(i)) cnt++; //又一个匹配了 38 } 39 return cnt; 40 } 41 42 43 int main() 44 { 45 freopen("input.txt", "r", stdin); 46 while(scanf("%d",&n), n) 47 { 48 scanf("%d%d",&m,&k); 49 memset(mapp,0,sizeof(mapp)); 50 memset(vis,0,sizeof(vis)); 51 memset(girl,0,sizeof(girl)); 52 for(int i=0; i<k; i++) 53 { 54 scanf("%d%d%d",&r,&a,&b); 55 //if(a>0&&b>0) 56 mapp[a][b]=1; //看作有向边,因为男女的编号可能相同的 57 } 58 printf("%d ",hungary()); //匈牙利算法 59 } 60 return 0; 61 }