算法理解参考:https://blog.youkuaiyun.com/sunny_hun/article/details/80627351
1.过山车
http://acm.hdu.edu.cn/showproblem.php?pid=2063
RPG girls今天和大家一起去游乐场玩,终于可以坐上梦寐以求的过山车了。可是,过山车的每一排只有两个座位,而且还有条不成文的规矩,就是每个女生必须找个个男生做partner和她同坐。但是,每个女孩都有各自的想法,举个例子把,Rabbit只愿意和XHD或PQK做partner,Grass只愿意和linle或LL做partner,PrincessSnow愿意和水域浪子或伪酷儿做partner。考虑到经费问题,boss刘决定只让找到partner的人去坐过山车,其他的人,嘿嘿,就站在下面看着吧。聪明的Acmer,你可以帮忙算算最多有多少对组合可以坐上过山车吗?
Input
输入数据的第一行是三个整数K , M , N,分别表示可能的组合数目,女生的人数,男生的人数。0<K<=1000
1<=N 和M<=500.接下来的K行,每行有两个数,分别表示女生Ai愿意和男生Bj做partner。最后一个0结束输入。
Output
对于每组数据,输出一个整数,表示可以坐上过山车的最多组合数。
Sample Input
6 3 3
1 1
1 2
1 3
2 1
2 3
3 1
0
Sample Output
3
//此题是求最大匹配数,用匈牙利算法
#include<stdio.h>
#include<string.h>
int line[505][505],boy[505],flag[505]; //line表示两人是否有关系,boy表示该男生的女伴,flag表示该男生是否被询问过
int n,m; //m为女生数量,n为男生数量
int find(int x) //为任意一个女生寻找最佳伴侣,找到则返回1
{
for(int k=1;k<=n;k++) //逐个扫描每个男生
if(line[x][k]==1&&flag[k]==0) //若两人之间有关系且在这次查找中没有被询问
{
flag[k]=1; //标志该男生在此次查找中已被询问
if(boy[k]==0||find(boy[k])) //如果该男生没有女伴或者该男生的女伴可以找到其他男生,则匹配成功,这是一个递归过程
{
boy[k]=x; //符合条件则将该女生和该男生匹配上,并标记
return 1;
}
}
return 0;
}
int main()
{
int k;
int i,a,b;
while(scanf("%d",&k)!=EOF&&k)
{
scanf("%d%d",&m,&n);
int count=0;
memset(line,0,sizeof(line));
memset(boy,0,sizeof(boy));
for(i=1;i<=k;i++)
{
scanf("%d%d",&a,&b);
line[a][b]=1; //表示女生a和男生b有关系
}
for(i=1;i<=m;i++) //逐个扫描每个女生,为她们寻找最佳伴侣
{
memset(flag,0,sizeof(flag)); //每次循环将标志都清零
if(find(i)) //如果找到,则最大匹配数加1
count++;
}
printf("%d\n",count);
}
return 0;
}
2.Air Raid
http://acm.hdu.edu.cn/showproblem.php?pid=1151
以一个城镇为例, 那里所有的街道都是单向的, 每条街道都从一个十字路口通往另一个十字路口。人们还知道, 从一个十字路口开始, 穿过城镇的街道, 你永远无法到达同一个十字路口, 即城镇的街道没有周期。
有了这些假设, 你的任务是写一个程序, 找到可以下到镇上的伞兵的最小数量, 并访问这个小镇的所有路口, 这样一个以上的伞兵访问没有交叉
口。每个伞兵都落在一个十字路口, 可以参观城镇街道之后的其他路口。对于每个伞兵的起始交叉口没有任何限制。
输入
您的程序应读取数据集。输入文件的第一行包含数据集的数量。每个数据集指定一个城镇的结构, 并具有格式:
没有 _ 交叉口没有 _ 街道
s1
e1 s2 e2
..。
sno _ of 街道 eno _ of
街道每个数据集的第一行包含 _ 交叉口的正整数 no _ (大于0和小于或等于 120), 即城镇中的交叉点的数量。第二行包含一个正整数 no _ of 街道, 即镇中的街道数量。下一个没有街道的线路, 镇上的每条街道都有一条, 是随机订购的, 代表着镇上的街道。与街道 k (k < = no _ 街道) 相对应的行由两个正整数组成, 由一个空白分隔: sk (1 < = sk < = _ 交叉口的十字路口的编号)--街道起点的交叉口的编号, 以及 ek (1 < = ek < = no _交叉点)-是街道尽头的交叉点的编号。交集由 _ 交叉点的1到 no _ 的整数表示。
连续的数据集之间没有空
行。输入数据是正确的。
输出
该程序的结果是在标准输出上。对于每个输入数据集, 程序打印在一行上, 从行的开头开始, 一个整数: 访问镇上所有路口所需的最小伞兵数。
样品输入
2
4
3
3 4
1 3
2 3
3
3
1 3
1 2
2 3
样品输出
2
1
题意:
城镇里的街道从一个交叉口连接到另一个交叉口,街道都是单向的,并且从一个交叉口沿着街道出发不会回到相同的交叉口。
伞兵降临在城镇的一个交叉口并可以沿着街道走向另一个没有被其他伞兵走过的交叉口
问城镇中的所有交叉口都被伞兵走过的情况下至少需要多少名伞兵。
要求:输入一个数字T,表示T组数据,然后每组数据前两行分别为一个数字n,m,n代表n个顶点,m表示m条边,求出在连通情况下的最少路径覆盖。
扩展题型
在一个有向无环图中,从一些顶点出发,能遍历到图上所有点,要求初始选择的顶点数最少且顶点不重复遍历。
思路:
如果从某个顶点开始遍历的过程看成是路径的选择
----------------------------------问题转化为-------------------------
在有向无环图中找最少的不想交的简单路径,这些路径覆盖图中的所有顶点。可见是关于最小路径覆盖的问题。
在有向无环图中,最小路径覆盖数 = 节点数 — 其对应二分图的最大匹配数。
最小路径覆盖它要求原图必须是有向无环图。
根据原图构造二分图,方法为:将原图中的每个顶点vi一分为二,相当于复制一个有向图,通过两个图的顶点根据数据关系进行匹配(把能匹配点的店匹配可构成便一条路),两个这就构造出一个路径覆盖问题所对应的二分图,在该二分图的基础上求其最大匹配数。
#include<bits/stdc++.h>
using namespace std;
int ship[500][500];
int vis[500];
int s[500];
int a,b;
int find(int x)
{
for(int i=1;i<=a;i++)
{
if(!vis[i]&&ship[i][x])
{
vis[i]=1;
if(!s[i]||find(s[i]))
{
s[i]=x;
return 1;
}
}
}
return 0;
}
int main()
{
int n;
cin>>n;
while(n--)
{
cin>>a>>b;
memset(ship,0,sizeof(ship));
memset(s,0,sizeof(s));
int y,x;
for(int i=1;i<=b;i++)
{
cin>>x>>y;
ship[x][y]=1;
}
int count=0;
for(int i=1;i<=a;i++)
{
memset(vis,0,sizeof(vis));
if(find(i))
count++;
}
int ans=a-count;
cout<<ans<<endl;
}
return 0;
}