zoj1137+作业1--2022年5月28日

zoj 1137

he second year of the university somebody started a study on the romantic relations between the students. The relation romantically involved is defined between one girl and one boy. For the study reasons it is necessary to find out the maximum set satisfying the condition: there are no two students in the set who have been romantically involved. The result of the program is the number of students in such a set.
The input contains several data sets in text format. Each data set represents one set of subjects of the study, with the following description:
the number of students
the description of each student, in the following format
student_identifier:(number_of_romantic_relations) student_identifier1 student_identifier2 student_identifier3 …
or
student_identifier:(0)

The student_identifier is an integer number between 0 and n-1, for n subjects.
For each given data set, the program should write to standard output a line containing the result.
An example is given in Figure 1.
Input
7
0: (3) 4 5 6
1: (2) 4 6
2: (0)
3: (0)
4: (2) 0 1
5: (1) 0
6: (2) 0 1
3
0: (2) 1 2
1: (1) 0
2: (1) 0

Output

5
2

题意描述:给你每个人相匹配的人,然后问最多能找到多少个人互不匹配。其实就是找:最大独立集合!

二分图最大独立集合 = 顶点个数 - 最小顶点覆盖(最大匹配)

分析:二分图最大匹配模板题。
二分图最大匹配模板题。

匈牙利算法

2. 匹配

图G的一个匹配是由一组没有公共端点的不是圈的边构成的集合。
这里,我们用一个图来表示下匹配的概念:
在这里插入图片描述
如图所示,其中的三条边即该图的一个匹配。所以,匹配的两个重点:1. 匹配是边的集合;2. 在该集合中,任意两条边不能有共同的顶点。
那么,我们自然而然就会有一个想法,一个图会有多少匹配?有没有最大的匹配(即边最多的匹配呢)?

3. 最大匹配

选择这样的边数最大的子集称为图的最大匹配问题。最大匹配的边数称为最大匹配。

4. 完美匹配

如果一个匹配中,图中的每个顶点都和图中某条边相关联,则称此匹配为完美匹配(完全匹配),也称作完备匹配。
考虑部集为X={x1 ,x2, …}和Y={y1, y2, …}的二分图,一个完美匹配就是定义从X-Y的一个双射,依次为x1, x2, … xn找到配对的顶点,最后能够得到 n!个完美匹配。

5. 最优匹配

最优匹配又称为带权最大匹配,是指在带有权值边的二分图中,求一个匹配使得匹配边上的权值和最大。
一般X和Y集合顶点个数相同,最优匹配也是一个完备匹配,即每个顶点都被匹配。如果个数不相等,可以通过补点加0边实现转化。一般使用KM算法解决该问题。

6. 最小覆盖

二分图的最小覆盖分为最小顶点覆盖和最小路径覆盖:

①最小顶点覆盖是指最少的顶点数使得二分图G中的每条边都至少与其中一个点相关联,二分图的最小顶点覆盖数=二分图的最大匹配数;

②最小路径覆盖也称为最小边覆盖,是指用尽量少的不相交简单路径覆盖二分图中的所有顶点。二分图的最小路径覆盖数=|V|-二分图的最大匹配数;

7. 最大独立集

最大独立集是指寻找一个点集,使得其中任意两点在图中无对应边。对于一般图来说,最大独立集是一个NP完全问题,对于二分图来说:最大独立集=|V|-二分图的最大匹配数。

8. 交替路

从未匹配点出发,依次经过未匹配的边和已匹配的边,即为交替路,如Fig.3:3 -> 5 -> 1 -> 7 -> 4 -> 8

在这里插入图片描述

9. 增广路(也称增广轨或交错轨)

如果交替路经过除出发点外的另一个未匹配点,则这条交替路称为增广路,如交替路概念的例子,其途径点8,即为增广路。
由增广路的定义推出下面三个结论(设P为一条增广路):
1). P的路径长度一定为奇数,第一条边和最后一条边都是未匹配的边(根据要途经已匹配的边和要经过另一个未匹配点,这个结论可以理解成第一个点和最后一个点都是未匹配点,可以在Fig.3上的增广路观察到)
2).对增广路径编号,所有奇数的边都不在M中,偶数边在M中。
3). P经过取反操作可以得到一个更大的匹配图,比原来匹配多一个(取反操作即,未匹配的边变成匹配的边,匹配的边变成未匹配的边,这个结论根据结论1).和交替路概念可得该结论)
4). 当且仅当不存在关于图M的增广路径,则图M为最大匹配。所以匈牙利算法的思路就是:不停找增广路,并增加匹配的个数。

二、匈牙利算法概述

匈牙利算法主要用来解决两个问题:求二分图的最大匹配数和最小点覆盖数。
我们寻找如上图的最大匹配。

(1)首先M集合为空(即没有边在里面),然后开始从X1寻找增广路,遵循上述原则我们只能在Yi中找,找到Y1,(X1,Y1 )这条路径,满足条件,取反,将(X1,Y1 )这条路径加入到M中。

在这里插入图片描述

(2)接着,我们找到X2点。遵循原则,找到Y1,Y1不是未覆盖点,这个时候我们有两种选择,一个是深度搜索,一个是广度搜索,我们采用深度优先,虽然Y1不是未覆盖点,(X2,Y1)不是增广路,但是Y1连着X1,X1又和Y3相连,我们考虑( X2,Y1,X1,Y3 )这条路径,奇数?左右交替?起终点未覆盖?奇路径不属于M偶路径属于?满足所有增广路条件,所以这是一条增广路径,然后取反,得到如下图。

在这里插入图片描述

3)现在M集合中的路径有两条了,由于我们找到了增广路径,使得M中的边数量增加。所以增广路径是匈牙利算法的核心,每找到一条增广路径,意味这M集合中边的数量就会增加1,当找不到增广路径的时候,这个时候M中边的数量就是我们二部图的最大匹配数量。

我们是怎样找到这条路径的呢,从X2开始寻找,我们先找到Y1,Y1不是未覆盖点,我们考虑Y1的原有匹配点X1,从X1开始寻找增广路,找到了Y3,当X1有增广路的时候,那么加上(X1,Y1)(X2,Y1)这两条路经,依然满足增广路条件。
所以基于我们上面的理解可以给出寻找增广路的伪代码:
  while(找到Xi的关联顶点Yj){
          if(顶点Yj不在增广路径上){
                将Yj加入增广路
               if(Yj是未覆盖点或者Yj的原匹配点Xk能找到增广路径){ //扩充集合M
                      将Yj的匹配点改为Xi;
                      返回true
           }
      }
               返回false
}

zoj 1137 匈牙利算法的板子题

#include<iostream>
#include<string.h>
using namespace std;
int map[1010][1010];
int visit[1010];
int match[1010];
int n;
int dfs(int u)
{
	int i;
	for (i = 0; i < n; i++)
	{
		if (visit[i] == 0 && map[u][i] == 1)//如果i未被访问且u与i匹配 
		{
			visit[i] = 1;//标记点i已被访问
			//如果点i未被配对或者找到了新的配对 
			if (match[i] == 0 || dfs(match[i]))
			{
				match[i] = u;//更新配对关系 
				return 1;
			}
		}
	}
	return 0;
}
int main()
{
	int m1, t, m, i, ans;
	while (cin>>n)
	{
		memset(map, 0, sizeof(map));
		memset(match, 0, sizeof(match));
		for (i = 0; i < n; i++)
		{
			cin>>m1>>t;
			while (t--)//读入边 
			{
				cin>>m;
				map[m1][m] = 1;
			}
		}
		ans = 0;
		for (i = 0; i < n; i++)
		{
			memset(visit, 0, sizeof(visit));//清空上次搜索时的记忆 
			if (dfs(i))//寻找增广路,如果找到,配对数加一 
				ans++;
		}
		printf("%d\n", n - ans / 2);//输出最大独立集数 
	}
	return 0;
}
/*
7
0 3 4 5 6
1 2 4 6
2 0
3 0
4 2 0 1
5 1 0
6 2 0 1
3
0 2 1 2
1 1 0
2 1 0
*/

zoj 1364

PTA zoj 1364
在这里插入图片描述
模式匹配;

A工厂和B工厂

题意:

有A,B两台机器,机器A有 n种工作模式,分别为 mode_0,mode_1,mode_2…,机器B有 m种工作模式,分别为 mode_0,mode_1,mode_2…。刚开始A,B的工作模式都是mode_0。

给定K个任务,表示为(i ,x,y),意思是作业 i 可以工作在机器A的mode_x模式或者机器B的mode_y的模式。

为了完成所有的工作,必须时不时的切换机器的工作模式,但机器工作模式的切换只能通过重启机器完成,问你最少重启多少次机器,才能把工作分配完。

解析:

一看有A ,B种机器,再根据题意,两种机器有匹配关系,我们首先构造二分图,把A的n个mode和B的m个mode看做图的顶点,如果某个任务可以在A的mode_i 或B的mode_j 上完成,则从Ai 到 Bj连一条边,这样就构成了二分图。
由题意可知,本意要求的是二分图的最小点覆盖集问题,即最小的顶点集合,**“覆盖”***所有的边,可以转化成二分图的最大匹配问题。
二分图的最小点覆盖数 == 最大匹配数。

另外的一个结论,与本题无关的: 最小边覆盖 = n - 最大匹配

另外要注意,机器A 和机器B最初都是在mode_0,所以对那些可以在机器A的mode_0或者机器B的模式_0工作的作业,在完成这些作业时是不需要重启机器的。所以从1开始遍历。看有几个点在一个部分里面在这里插入图片描述

在这里插入图片描述

cin>>cost>>m1>>t;

# map[m1][t] = 1;
# 	//map[t][m1] = 1;  行是A , 列是B 不能对称赋值。

比如 A的3 可以和B 的2 一起做 JOB3

不代表 A的 2可以和B 的 3 一起做JOB3

#include<iostream>
#include<string.h>
using namespace std;
const int N = 105;
int map[N][N];
int visit[N];
int a[N];
int b[N];
int nodenum;
int dfs(int u)
{
	int i;
	for (i = 1; i < nodenum; i++)   //按行扫描
	{
		//每次判断序号小的点是否相邻,是从左往右扫描的
		if (visit[i] == 0 && map[u][i] == 1)//如果i未被访问且u与i匹配 
		{
			visit[i] = 1;//标记点i已被访问
			//如果点i未被配对或者找到了新的配对 
			//机器a的模式u与机器b的模式i匹配
			if (b[i] == -1 || dfs(b[i]))
			{
				a[u] = i;      //更新配对关系 
				b[i] = u;
				return 1;
			}
		}
	}
	return 0;
}
int main()
{
	int m1, t, m, i, ans,cost,s=0,edgenum;
	while (cin>>s)
	{
		if (s == 0)
			return 0;
		cin >> nodenum;
		cin >> edgenum;
		memset(map, 0, sizeof(map));
		for (i = 0; i < edgenum; i++)
		{
			cin>>cost>>m1>>t;
			map[m1][t] = 1;
			//map[t][m1] = 1;  行是A , 列是B 不能对称赋值。
		}
		ans = 0;
		memset(a, -1, sizeof(a));
		memset(b, -1, sizeof(b));
		for (i = 1; i < s; i++)
		{
			if (a[i] == -1)
			{
				memset(visit, 0, sizeof(visit));//清空上次搜索时的记忆 
			//寻找增广路,如果找到,配对数加一 
					ans+=dfs(i);
			}
		}
		printf("%d\n", ans );//输出最大匹配数
	}
	return 0;
}
/*
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
*/
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值