再来更新一下,今天做题又遇见了二分图,在这里补充个板子:
int dfs(int x){
for(int i=1;i<=n;i++)
{
if(可以匹配&&预定男孩i给女孩!vis[i])
{
vis[i]=1; 保证这个男孩不被其他女孩抢走。
if(当前男孩没有被匹配)
{
match[i]=x;
return true;
}
else if(dfs(match[i])) 如果被匹配了就让另一个女孩更换。
{
match[i]=x;
return true;
}
}
}
return false;
}
更新一下对二分图新的理解,其实开始做的时候就有感觉了。在进行当前更新时,仅对结果而言,宏观上来讲既不会影响当前的状态,也不会影响之后的选择,也就不影响最后的结果。
Problem Description 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
同之前一样,了解了二分图最大匹配的概念后就试着去自己实现。但这次没上次那么幸运了,敲了一下午最后不得不看看匈牙利算法的实现,发现自己主要错在dfs的循环节判断上,以下这个代码是第一次敲完的代码。这个代码死在在进行dfs深层搜索时,没有排除自己已经匹配的男生,导致男生的对象还是自己,自己又要更换男生,换到唯一 一个男生的对象还是自己,最后形成无限递归。
hack样例:
4 4 4
1 1
1 2
2 1
3 1
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
#include<algorithm>
#include<cmath>
#include<map>
#include<queue>
using namespace std;
const int maxn = 505;
int G[maxn][maxn];
int matc[maxn];
int k, m, n;
int match[maxn];
int dfs(int x) {
for (int i = 1; i <= n; i++) //女孩x对n个男生开始匹配
{
if (G[x][i] && !matc[i])
{
matc[i] = x;
return true;
}
}
for (int i = 1; i <= n; i++) {
if (G[x][i]) {
if(dfs(matc[i])) {
matc[i] = x;
return true;
}
}
}
return false;
}
//
int main() {
while (cin >>k&&k) {
cin >> m >> n;
memset(G, 0, sizeof(G));
memset(matc, 0, sizeof(matc));
for (int i = 1; i <= k; i++) {
int a, b;
cin >> a >> b;
G[a][b] = 1;
}
int sum = 0;
for (int i = 1; i <= m; i++) {
memset(match, 0, sizeof(match));
if (dfs(i))
sum++;
}
cout << sum << endl;
}
return 0;
}
经过略微改进后添加了一个步骤——对男生的对象是否是自己进行判断,如果是则直接跳过,这又会排除一些测试例子。但还是不行,在后面我看了匈牙利实现的方法后,自己造了一个例子:如果女2喜欢男1和男2,女3也喜欢男1和男2,现在女x和要和男1进行匹配,那么女2要让位,搜索男2,那么女3又要让位搜索男1,由于男1并未更新,又搜到了女2的头上,而女2又搜男2,搜到了女3的头上,由此产生无限递归,
hack样例:
5 4 4
2 1
2 2
3 2
3 1
4 1
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
#include<algorithm>
#include<cmath>
#include<map>
#include<queue>
using namespace std;
const int maxn = 505;
int G[maxn][maxn];
int matc[maxn];
int k, m, n;
int match[maxn];
int dfs(int x) {
for (int i = 1; i <= n; i++) //女孩x对n个男生开始匹配
{
if (G[x][i] && !matc[i])
{
matc[i] = x;
return true;
}
}
for (int i = 1; i <= n; i++) {
if (G[x][i]) {
if (matc[i] == x)continue;
if(dfs(matc[i])) {
matc[i] = x;
return true;
}
}
}
return false;
}
//
int main() {
while (cin >>k&&k) {
cin >> m >> n;
memset(G, 0, sizeof(G));
memset(matc, 0, sizeof(matc));
for (int i = 1; i <= k; i++) {
int a, b;
cin >> a >> b;
G[a][b] = 1;
}
int sum = 0;
for (int i = 1; i <= m; i++) {
memset(match, 0, sizeof(match));
if (dfs(i))
sum++;
}
cout << sum << endl;
}
return 0;
}
最后看了下匈牙利算法的实现,增添了一个标记数组,过了这道题。哭死。。但是这道题给我启发还挺大的,之前一直不怎么关注MLT,我在hdu提交时,由于之前的代码产生了无限递归,结果MLT了,但数组就那么点,查阅了一番才明白,题目所给的空间限制不只是你在程序中开辟的空间,还包含程序运行时所占用的临时空间,而在递归时,由于要保存上一层递归的数据,无疑这需要额外的临时空间,又刚好我进行了无限递归,导致需要的临时空间无限大,结果MLT了。递归的空间复杂度是O(n),我也不知道怎么算的。
是时候去学学空间复杂度了。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <vector>
#include <stack>
#include<algorithm>
#include<cmath>
#include<map>
#include<queue>
using namespace std;
const int maxn = 505;
int G[maxn][maxn];
int matc[maxn];
int k, m, n;
int match[maxn];
int dfs(int x) {
for (int i = 1; i <= n; i++) //女孩x对n个男生开始匹配
{
if (!match[i]&&G[x][i])
{
match[i] = 1;
if (!matc[i])
{
matc[i] = x;
return true;
}
else {
if (dfs(matc[i])) {
matc[i] = x;
return true;
}
}
}
}
return false;
}
// if (matc[i] == x)continue; //除了自己之外找一个能匹配的
int main() {
while (cin >>k&&k) {
cin >> m >> n;
memset(G, 0, sizeof(G));
memset(matc, 0, sizeof(matc));
for (int i = 1; i <= k; i++) {
int a, b;
cin >> a >> b;
G[a][b] = 1;
}
int sum = 0;
for (int i = 1; i <= m; i++) {
memset(match, 0, sizeof(match));
if (dfs(i))
sum++;
}
cout << sum << endl;
}
return 0;
}