转载请注明本文地址
学习了二分图的邻接矩阵存储方式,原来二维刚好表示二分图……不是按照所有的点的存法
题意:有两个机器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;
}