最近一直在恶补二分图的内容。前面花了一个星期基本的了解了二分图的定义、性质以及染色法判定二分图。
根据二分图的一些性质又有求二分图的最大匹配数。匈牙利求最大匹配。
二分图及匹配算法还未涉及的有:匹配基本定理(Berge定理、Hall定理)、Hopcroft-Karp算法、二分图多重匹配、二分图最大匹配的网络流解法。具体更多看《图论及应用》这本书的第六章。
我直接跳到了第六章的第五节。前面的以后再倒回去学吧。我的想法是刚学了二分图和匈牙利想去刷一刷类似的题。
定义:假如选了一个点就相当于覆盖了以它为端点的所有边。最小顶点覆盖就是选择最少的点来覆盖所有的边。
方法:最小顶点覆盖等于二分图的最大匹配。
来看一道题:
Time Limit: 1000MS | Memory Limit: 10000K | |
Total Submissions: 17840 | Accepted: 7451 |
Description
As we all know, machine scheduling is a very classical problem in computer science and has been studied for a very long history. Scheduling problems differ widely in the nature of the constraints that must be satisfied and the type of schedule desired. Here we consider a 2-machine scheduling problem.
There are two machines A and B. Machine A has n kinds of working modes, which is called mode_0, mode_1, ..., mode_n-1, likewise machine B has m kinds of working modes, mode_0, mode_1, ... , mode_m-1. At the beginning they are both work at mode_0.
For k jobs given, each of them can be processed in either one of the two machines in particular mode. For example, job 0 can either be processed in machine A at mode_3 or in machine B at mode_4, job 1 can either be processed in machine A at mode_2 or in machine B at mode_4, and so on. Thus, for job i, the constraint can be represent as a triple (i, x, y), which means it can be processed either in machine A at mode_x, or in machine B at mode_y.
Obviously, to accomplish all the jobs, we need to change the machine's working mode from time to time, but unfortunately, the machine's working mode can only be changed by restarting it manually. By changing the sequence of the jobs and assigning each job to a suitable machine, please write a program to minimize the times of restarting machines.
Input
The input file for this program consists of several configurations. The first line of one configuration contains three positive integers: n, m (n, m < 100) and k (k < 1000). The following k lines give the constrains of the k jobs, each line is a triple: i, x, y.
The input will be terminated by a line containing a single zero.
Output
The output should be one integer per line, which means the minimal times of restarting machine.
Sample Input
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
Sample Output
3
题意解释:
我们知道机器调度是计算机科学中一个非常经典的问题。调度问题有很多种,具体条件不同,问题就不同。现在我们要处理的是两个机器的调度问题。
有两个机器A和B。机器A有n种工作模式,我们称之为mode_0,mode_l,……,mode_n-1。同样,机器B有m种工作模式,我们称之为mode_0,mode_1,……,mode_m-1。初始时,两台机器的工作模式均为mode_0。现在有k个任务,每个工作都可以在两台机器中任意一台的特定的模式下被加工。例如,job0能在机器A的mode_3或机器B的mode_4下被加工,jobl能在机器A的mode_2或机器B的mode_4下被加工,等等。因此,对于任意的jobi,我们可以用三元组(i,x,y)来表示jobi在机器A的mode_x或机器B的mode_y下被加工。
显然,要完成所有工作,我们需要不时的改变机器的工作模式。但是,改变机器的工作状态就必须重启机器,这是需要代价的。你的任务是,合理的分配任务给适当的机器,使机器的重启次数尽量少。
大意:有A、B两台机器和k个任务,机器A有n种模式,B有m种模式。初始状态都为0.每个任务都可由机器A的一种模式或B
的一种模式执行。每切换一次模式,需要代价1,问最小的代价。
做法:将机器A和机器B的每个模式都看成一个点。某个任务看做一条边。那么问题就转换成是否存在一个最小规模的点集,使得所有的边都至少和该点集合中的一个点相关联。
前面提到:最小点覆盖等于二分图的最大匹配。匈牙利求最大匹配即可
AC代码:
#include
#include
#include
using namespace std;
const int N=120;
int ma[N][N],n,m,k,vis[N],part[N];
bool find(int x)
{
for(int i=1;i<=110;i++)
{
if(ma[x][i]&&!vis[i])
{
vis[i]=1;
if(!part[i]||find(part[i]))
{
part[i]=x;
return 1;
}
}
}
return 0;
}
int slove()
{
int ans=0;
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof(vis));
if(find(i)) ans++;
}
return ans;
}
int main()
{
while(scanf("%d",&n)&&n)
{
scanf("%d%d",&m,&k);
memset(ma,0,sizeof(ma));
memset(part,0,sizeof(part));
for(int i=1;i<=k;i++)
{
int u,v,w;
scanf("%d%d%d",&w,&u,&v);
ma[u][v]=1;
}
printf("%d\n",slove());
}
}
定义:在一个有向图中,找出最少的路径,使得这些路径经过了所有的点。
最小路径覆盖分为最小不相交路径覆盖和最小可相交路径覆盖。
最小不相交路径覆盖:每一条路径经过的顶点各不相同。如图,其最小路径覆盖数为3。即1->3>4,2,5。
最小可相交路径覆盖:每一条路径经过的顶点可以相同。如果其最小路径覆盖数为2。即1->3->4,2->3>5。
特别的,每个点自己也可以称为是路径覆盖,只不过路径的长度是0。
来看一道题:
Air RaidTime Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 6987 Accepted Submission(s): 4630 Problem Description Consider a town where all the streets are one-way and each street leads from one intersection to another. It is also known that starting from an intersection and walking through town's streets you can never reach the same intersection i.e. the town's streets form no cycles. Input Your program should read sets of data. The first line of the input file contains the number of the data sets. Each data set specifies the structure of a town and has the format:
Output The result of the program is on standard output. For each input data set the program prints on a single line, starting from the beginning of the line, one integer: the minimum number of paratroopers required to visit all the intersections in the town.
Sample Input 2 4 3 3 4 1 3 2 3 3 3 1 3 1 2 2 3 Sample Output 1 |
这题是属于 最小不相交路径覆盖。
最小可相交路径覆盖以后待补.........
题目意思:
题目描述:城镇里的街道从一个交叉口连接到另一个交叉口,街道都是单向的,并且从一个交叉口沿着街道出发不会回到相同的交叉口。伞兵降临在城镇的一个交叉口并可以沿着街道走向另一个没有被其他伞兵走过的交叉口,问城镇中的所有交叉口都被伞兵走过的情况下至少需要多少名伞兵。
题目的大意是在一个有向无环中,从一些顶点出发,能遍历图上的所有顶点,要求初始选择的顶点数最少且顶点不重复遍历。
做法:将每个点都分成两个顶点,一个在左,一个在右。若Vi到Vj有一条有向边,那就在左边的Vi连一条线到右边的Vj。
这样就构造了一个路径覆盖问题所对应的二分图形式。
前面的结论:有向无环图的最小路径覆盖=节点数-其拆点后对应二分图的最大匹配数
AC代码:
#include
using namespace std;
const int N=150;
int vis[N],part[N];
int ma[N][N],t,n,m;
bool find(int x)
{
for(int i=1;i<=n;i++)
{
if(ma[x][i]&&!vis[i])
{
vis[i]=1;
if(!part[i]||find(part[i]))
{
part[i]=x;
return 1;
}
}
}
return 0;
}
int slove()
{
int ans=0;
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof(vis));
if(find(i)) ans++;
}
return ans;
}
int main()
{
cin>>t;
while(t--)
{
memset(part,0,sizeof(part));
memset(ma,0,sizeof(ma));
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++)
{
int u,v;
scanf("%d%d",&u,&v);
ma[u][v]=1;
}
printf("%d\n",n-slove());
}
}
定义:选出一些顶点使得这些顶点两两不相邻,则这些点构成的集合称为独立集。找出一个包含顶点数最多的独立集称为最大独立集。
方法:最大独立集=所有顶点数-最小顶点覆盖
普通图的最大团=补图的最大独立点集=所有节点数-二分图中最大匹配数(补图为二分图)
最小顶点覆盖==二分图中最大匹配数
KindergartenTime Limit: 5000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 1539 Accepted Submission(s): 810 Problem Description In a kindergarten, there are a lot of kids. All girls of the kids know each other and all boys also know each other. In addition to that, some girls and boys know each other. Now the teachers want to pick some kids to play a game, which need that all players know each other. You are to help to find maximum number of kids the teacher can pick. Input The input consists of multiple test cases. Each test case starts with a line containing three integers Output For each test case, print a line containing the test case number( beginning with 1) followed by a integer which is the maximum number of kids the teacher can pick.
Sample Input 2 3 3 1 1 1 2 2 3 2 3 5 1 1 1 2 2 1 2 2 2 3 0 0 0 Sample Output Case 1: 3 Case 2: 4 |
题意:有一群孩子,男孩之间互相认识,女孩之间互相认识,另外有部分男孩和女孩也是认识的。现在老师需要选出一个孩子集合,集合中所有的孩子都互相认识。求集合的最大元素个数。
首先我们可以通过题目的输入构建一张图,如果两个人认识,就记为1。不认识就记为0。接下来我们把图转化为补图,那么补图中有边就表示这两个人其实是不认识的,那么我们要做的就是求最小顶点覆盖(找最少的顶点覆盖所有的边),把这些顶点去除后补图中就没有边了,那么就表示在原图中这些人是互相认识的。
【再来看这题,题目中,男生是一个点集,男生都互相认识,女生是一个点集,女生都互相认识,则这个图的补图肯定是二分图。因为补图的边一个端点在男生点集,一个端点在女生点集。确认是二分图后,求二分图的最大独立集】
AC代码:
#include
using namespace std;
const int N=220;
int vis[N],part[N];
int ma[N][N];
int n,m,k;
bool dfs(int x)
{
for(int i=1;i<=m;i++)
{
if(ma[x][i]&&!vis[i])
{
vis[i]=1;
if(!part[i]||dfs(part[i]))
{
part[i]=x;
return 1;
}
}
}
return 0;
}
int slove()
{
int ans=0;
for(int i=1;i<=n;i++)
{
memset(vis,0,sizeof(vis));
if(dfs(i)) ans++;
}
return ans;
}
int main()
{
int cas=0;
while(cin>>n>>m>>k&&n&&m&&k)
{
memset(part,0,sizeof(part));
memset(ma,1,sizeof(ma));
for(int i=1;i<=k;i++)
{
int u,v;
scanf("%d%d",&u,&v);
ma[u][v]=0;
}
printf("Case %d: %d\n",++cas,n+m-slove());
}
}
最小点权覆盖后续再补......