这几个题是我觉得比较好的入门二分多重匹配的题。
什么是二分多重匹配?
简单的来说,就是一个对于左边(或者右边)的点集,每个点都要有单独输出的完备匹配,但是其对应的另一侧点集,每个点可以有多个输入(有些点甚至可能没有输入也有可能),简单的来说就是:目标方可以有多个输入吧,输出方每个只能单个输出,保障每个输出方点均有输出。
既然是要多重,那么我们就会有一定的约束条件。比如POJ2289:意思就是每个人都要有对应的点,你要保证最大点的集合中包含人的数量最少。这就是一种限制性条件。那么怎么去解决这个问题呢?这时候就需要运用二分枚举了,我们可以通过二分最大点包含人的数量的个数来进行收缩,最后当l==r时,这个r就是我们索要求的结果了。二分方法比较好理解。
那么我们该怎么去找想要的多重匹配呢?这其实是匈牙利算法的一种优化。我们给出当前限制条件 d(也就是二分的mid),通过记录目标点v接受次数及其对应所接受的u来进行匈牙利算法,如果v的次数len[v] < d,那么我们还可以通过添加增广路使len[v]++,如果len[v]==d了,那么我们可以利用匈牙利算法的思维,去遍历0~len[v]-1,这里面的u,通过改变u的交替路径来使当前增广路成立,如果无论怎么改都不成立,那么就结束了。此时即是最大情况了。
对于例题POJ2112来说,这也是一个很经典的多重匹配问题。他把牛的集合当做发送集合,棚的集合当做接受集合,二分的对象是路径(怎么求最小路径?用Floyd算法求多源最短路即可),每个棚最多塞k个(多重匹配的约束条件是k),然后当你求MatchMax的时候,记住当前投入的都是可以到达的路径(反正求得的最短路径>二分约束条件d的我都当做不可到达),并添加至mp[u][v]数组之中。这样问题就简化成和POJ2289一样的很裸的板子问题了。
对于例题POJ3189来说,同样很经典,二分的是距离len(1-B中),与以上两题不同的是每一个点都有其专属的约束条件limit[i]。我们期望二分的mid(len)尽可能的小,所以需要另外的一种for遍历,遍历[i,i+len-1],去抠范围,而此时上面的L=i,R=i+len-1所对应的就是1-B中的范围。这题匈牙利算法运用范围是L-R,和上面2题1-N又有点不一样,这个L,R怎么来的我刚才已经说了。所以这题还是很经典的吧。
然后我展示一下POJ2112的AC代码吧(用作板子了)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#define ll long long
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,n,a) for(int i=n;i>=a;i--)
#define endl '\n'
#define mem(a) memset(a,0,sizeof(a))
#define IO ios::sync_with_stdio(false);cin.tie(0);
using namespace std;
const int INF=0x3f3f3f3f;
const int mod=1e9+