转载请注明本文地址
学习了二分图的邻接矩阵存储方式,原来二维刚好表示二分图……不是按照所有的点的存法
题意:有两个机器A和B,分别有mode_0,mode_1...mode_n-1和mode_0,mode_1...mode_m-1,初始均在mode_0。现在有k个job,每个job可以在A的mode_x或者B的mode_y下完成。每次重新选择模式要重启机器。问最少重启多少次机器能完成所有工作。
思路:
1.首先要注意一开始就是在mode_0,所以输入中如果有可以在mode_0下完成的就不用理睬了。
2.把A的n种模式看成n个点,属于一个集合,B的m种模式看成m个点,属于另一个集合。然后一个(x,y)就表示两个集合之间有连接A的mode_x与B的mode_y的边。然后就是要求最小点覆盖。
注:最小顶点覆盖是这个意思:对每条边,其两个顶点中的任一个是“存在”的,那视作这条边“被覆盖”,现在用最少的顶点数,使所有边都被覆盖(也就是使所有边的至少一个端点“存在”)。
3.无向图中求最小顶点覆盖问题是NP-hard问题,但是二分图中其等于最大匹配数(证明见这里),所以直接求一遍最大匹配就可以了。
4.注意邻接矩阵的二维刚好存二分图的二维……而不是存所有的顶点。
代码:
#include<iostream> #include<cstdio> #include<cstring> #include<cstdlib> #include<cmath> using namespace std; const int MAXV=110; bool map[MAXV][MAXV],vis[MAXV]; int match[MAXV]; int n,m,k; void init() { memset(map,false,sizeof(map)); memset(match,-1,sizeof(match)); } bool DFS(int u) { for(int v=0;v<n;v++) { if(map[u][v] && !vis[v]) { vis[v]=true; if(match[v]==-1 || DFS(match[v])) { match[v]=u; return true; } } } return false; } int hungary() { int ans=0; for(int u=0;u<n;u++) { memset(vis,false,sizeof(vis)); if(DFS(u)) ans++; } return ans; } int main() { while(scanf("%d",&n),n) { scanf("%d%d",&m,&k); init(); int index,x,y; for(int i=0;i<k;i++) { scanf("%d%d%d",&index,&x,&y); if(x && y) { x--,y--; map[x][y]=true; } } printf("%d\n",hungary()); } return 0; }