二部图算法
最大二分匹配
匈牙利算法
算法思路:
代码(template):
#include<iostream>
#include<vector>
#include<bits/stdc++.h>
using namespace std;
#define maxn 105
int G[maxn][maxn];//存储图
bool vis[maxn];//访问数组
int match[maxn];//匹配数组
vector<int> V[maxn];//存储每个点的相邻节点
bool dfs(int start) {//深度优先搜索寻找匹配点
for (int i = 0; i<int(V[start].size()); i++) {//遍历每一个与start相邻的节点
int v = V[start][i];//存储与start相邻的节点
if (!vis[v]) {//假如没有被访问过,才可进一步寻求匹配
vis[v] = 1;//标记访问
int tmp = match[v];//存储目前v的匹配点
match[v] = start;//给v新的匹配点start
if (tmp == -1 || dfs(tmp)) {//如果v没有匹配或原来v的匹配点能够匹配到其他节点
return true;//说明v可以匹配start
}
match[v] = tmp;//如果不能匹配start,则回退,让v的匹配恢复原来
}
}
return false;//从start节点找不到匹配顶点
}
int main() {
int n1, n2;
while (cin >> n1 >> n2 && n1 != 0 && n2 != 0) {
//建图
memset(G, -1, sizeof(G));
int m;
cin >> m;
while (m--) {
int a, b;
cin >> a >> b;
G[a][b] = 0;
}
int num = 0;
for (int i = 1; i <= n1; i++) {
for (int j = 1; j <= n2; j++) {
if (G[i][j] == -1) {
G[i][j] = ++num;
}
}
}
for (int i = 1; i <= num; i++) {//初始化每一个顶点的相邻节点
V[i].clear();
}
//横
for (int i = 1; i <= n1; i++) {
for (int j = 1; j <= n2 - 1; j++) {
if (G[i][j] != 0 && G[i][j + 1] != 0) {
V[G[i][j]].push_back(G[i][j + 1]);
V[G[i][j + 1]].push_back(G[i][j]);
}
}
}
//竖
for (int j = 1; j <= n2; j++) {
for (int i = 1; i <= n1 - 1; i++) {
if (G[i][j] != 0 && G[i + 1][j] != 0) {
V[G[i][j]].push_back(G[i + 1][j]);
V[G[i + 1][j]].push_back(G[i][j]);
}
}
}
//最大匹配
int ans = 0;//初始化解
memset(match, -1, sizeof(match));//初始化匹配数组
for (int i = 1; i <= num; i++) {
memset(vis, 0, sizeof(vis));//初始化访问数组
if (dfs(i)) ans++;//若存在匹配,则ans++
}
cout << ans / 2 << endl;//每个顶点都进行了匹配,因此最后一条边匹配了2次,所以要/2
}
return 0;
}
相关例题
zoj1137 Girls and boys
#include<bits/stdc++.h>
using namespace std;
#define maxn 10005
bool vis[maxn];//访问
int match[maxn];//匹配
set<int> V[maxn];//记录每个点的邻接顶点集
bool dfs(int start)
{//深搜的含义是能够找到下一个节点进行匹配
for (set<int>::iterator it = V[start].begin(); it != V[start].end(); it++) //逐个节点进行分析,分析是否能匹配
{
int v = *it;
if (!vis[v])
{ //若没有被访问过
vis[v] = 1;
int tmp = match[v]; //保留当前的匹配情况
match[v] = start; //尝试填写新的匹配关系
if (tmp == -1 || dfs(tmp))
{ //分析这个新匹配是否有效
return true;
}
match[v] = tmp;
}
}
return false;
}
int str2int(string str){
int ans = 0;
for (int i = 0; i < int(str.size()); i++) {
ans = ans * 10 + str[i] - '0';
}
return ans;
}
int substr(string str) {//获取()里面的数字
string s = "";
for (int i = 0; i < int(str.size()); i++) {
if (i != 0 && i != int(str.size()) - 1) {
s += str[i];
}
}
return str2int(s);
}
int main() {
//建图
int n;
while (cin >> n) {
for(int i=0;i<n;i++){
V[i].clear();
}
for (int i = 0; i < n; i++) {
string str;
cin >> str;//读入0:
cin >> str;//读入()
int m = substr(str);
while (m--) {//注意这里,直接改int就不用str转int
int nn;
cin >> nn;
V[i].insert(nn);
}
}
memset(match, -1, sizeof(match));
//深搜遍历
for (int i = 0; i < n; i++)
{
memset(vis, 0, sizeof(vis));
dfs(i);
}
//求出最大匹配数
int num = 0; //记录匹配个数
for (int i = 0; i < n; i++)
{
if (match[i] != -1)
num++;
}
cout << n - (num / 2) << endl;
}
return 0;
}
zoj1002 Fire Net
#include <iostream>
#include <bits/stdc++.h>
#include <vector>
using namespace std;
#define maxn 100
int Gr[maxn][maxn];
int Gc[maxn][maxn];
char G[maxn][maxn];
vector<int> V[maxn]; //点集
bool vis[maxn]; //标记访问
int match[maxn]; //标记匹配
int cnt2; //记录最大点
bool dfs(int start)
{ //深搜的含义是能够找到下一个节点进行匹配
for (int i = 0; i < V[start].size(); i++) //逐个节点进行分析,分析是否能匹配
{
int v = V[start][i];
if (!vis[v])
{ //若没有被访问过
vis[v] = 1;
int tmp = match[v]; //保留当前的匹配情况
match[v] = start; //尝试填写新的匹配关系
if (tmp == -1 || dfs(tmp))
{ //分析这个新匹配是否有效
return true;
}
match[v] = tmp;
}
}
return false;
}
int main()
{
int n;
while (cin >> n && n)
{
int cnt1 = 0; //开两个计数器
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
cin >> G[i][j];
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (G[i][j] == 'X')
{
Gr[i][j] = 0; //表示黑格
Gc[i][j] = 0;
}
else
{
if (j == 1 || (j != 1 && G[i][j - 1] == 'X'))
cnt1++;
Gr[i][j] = cnt1; //横
}
}
}
cnt2 = cnt1;
for (int j = 1; j <= n; j++)
{
for (int i = 1; i <= n; i++)
{
if (G[i][j] == 'X')
{
continue;
}
else
{
if (i == 1 || (i != 0 && G[i - 1][j] == 'X'))
cnt2++;
Gc[i][j] = cnt2; //列
}
}
}
for (int i = 1; i <= cnt2; i++)
{
V[i].clear();
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if (G[i][j] == 'X')
continue;
else
{
V[Gr[i][j]].push_back(Gc[i][j]);
V[Gc[i][j]].push_back(Gr[i][j]);
}
}
}
// for(int i=0;i<n;i++){
// for(int j=0;j<n;j++){
// cout<<Gr[i][j]<<" ";
// }
// cout<<endl;
// }
// for(int i=0;i<n;i++){
// for(int j=0;j<n;j++){
// cout<<Gc[i][j]<<" ";
// }
// cout<<endl;
// }
// for(int i=0;i<=cnt2;i++){
// for(int j=0;j<V[i].size();j++){
// cout<<V[i][j]<<" ";
// }
// cout<<endl;
// }
memset(match, -1, sizeof(match));
for (int i = 1; i <= cnt2; i++)
{
memset(vis, 0, sizeof(vis));
dfs(i);
}
int num = 0; //记录匹配个数
for (int i = 1; i <= cnt2; i++)
{
if (match[i] != -1)
num++;
}
cout << num / 2 << endl;
}
return 0;
}
zoj1654 Place the Robots
#include<bits/stdc++.h>
using namespace std;
#define maxn 5555
char G[maxn][maxn];
int Gr[maxn][maxn];
int Gc[maxn][maxn];
bool vis[maxn];
int match[maxn];
vector<int> V[maxn];
bool dfs(int start){
for(int i=0;i<int(V[start].size());i++){
int v = V[start][i];
if(!vis[v]){
vis[v] = 1;
int tmp = match[v];
match[v] = start;
if(tmp==-1 || dfs(tmp)){
return true;
}
match[v] = tmp;
}
}
return false;
}
int main(){
int m;
cin>>m;
for(int w=1;w<=m;w++){
int r,c;
cin>>r>>c;
for(int i=0;i<r;i++){
cin>>G[i];
}
//建立图模型
int cnt1=0,cnt2;
//横
for(int i=0;i<r;i++){
for(int j=0;j<c;j++){
if(G[i][j]=='#'){//不能放
Gc[i][j] = Gr[i][j] = 0;
}else{
if(j==0 || (j!=0 && G[i][j-1]=='#')){//若为第一列或不是第一列但前面有#
cnt1++;
}
Gr[i][j] = cnt1;
}
}
}
//竖
cnt2 = cnt1;
for(int j=0;j<c;j++){
for(int i=0;i<r;i++){
if(G[i][j]!='#'){
if(i==0 || (i!=0 && G[i-1][j]=='#')){//若为第一行或不是第一行但上面有#
cnt2++;
}
Gc[i][j] = cnt2;
}
}
}
//建图
for(int i=0;i<cnt2;i++){
V[i].clear();
}
for(int i=0;i<r;i++){
for(int j=0;j<c;j++){
if(G[i][j]=='o'){//只有为o才可放
V[Gr[i][j]].push_back(Gc[i][j]);
V[Gc[i][j]].push_back(Gr[i][j]);
}
}
}
//求最大匹配
int ans=0;
memset(match,-1,sizeof(match));
for(int i=1;i<=cnt2;i++){
memset(vis,0,sizeof(vis));
if(dfs(i)){
ans++;
}
}
cout<<"Case :"<<w<<endl;
cout<<ans/2<<endl;
}
return 0;
}
zoj1516 Uncle Tom's Inherited Land
#include<iostream>
#include<vector>
#include<bits/stdc++.h>
using namespace std;
#define maxn 105
int G[maxn][maxn];
bool vis[maxn];
int match[maxn];
vector<int> V[maxn];
bool dfs(int start) {
for (int i = 0; i<int(V[start].size()); i++) {
int v = V[start][i];
if (!vis[v]) {
vis[v] = 1;
int tmp = match[v];
match[v] = start;
if (tmp == -1 || dfs(tmp)) {
return true;
}
match[v] = tmp;
}
}
return false;
}
int main() {
int n1, n2;
while (cin >> n1 >> n2 && n1 != 0 && n2 != 0) {
//建图
memset(G, -1, sizeof(G));
int m;
cin >> m;
while (m--) {
int a, b;
cin >> a >> b;
G[a][b] = 0;
}
int num = 0;
for (int i = 1; i <= n1; i++) {
for (int j = 1; j <= n2; j++) {
if (G[i][j] == -1) {
G[i][j] = ++num;
}
}
}
for (int i = 1; i <= num; i++) {//初始化V
V[i].clear();
}
//横
for (int i = 1; i <= n1; i++) {
for (int j = 1; j <= n2 - 1; j++) {
if (G[i][j] != 0 && G[i][j + 1] != 0) {
V[G[i][j]].push_back(G[i][j + 1]);
V[G[i][j + 1]].push_back(G[i][j]);
}
}
}
//竖
for (int j = 1; j <= n2; j++) {
for (int i = 1; i <= n1 - 1; i++) {
if (G[i][j] != 0 && G[i + 1][j] != 0) {
V[G[i][j]].push_back(G[i + 1][j]);
V[G[i + 1][j]].push_back(G[i][j]);
}
}
}
//最大匹配
int ans = 0;
memset(match, -1, sizeof(match));
for (int i = 1; i <= num; i++) {
memset(vis, 0, sizeof(vis));
if (dfs(i)) ans++;
}
cout << ans / 2 << endl;
}
return 0;
}
zoj1364 Machine Schedule
#include<bits/stdc++.h>
using namespace std;
#define maxn 205
bool vis[maxn];
int match[maxn];
set<int> V[maxn];
bool dfs(int start){
for(set<int>::iterator it=V[start].begin();it!=V[start].end();it++){
int v = *it;
if(!vis[v]){
vis[v] = 1;
int tmp = match[v];
match[v] = start;
if(tmp==-1 || dfs(tmp)){
return true;
}
match[v] = tmp;
}
}
return false;
}
int main(){
int n,m,k;
while(cin>>n>>m>>k && n){
//初始化
for(int i=0;i<n+m;i++){
V[i].clear();
}
//建图
while(k--){
int a,b,c;
cin>>a>>b>>c;
V[b].insert(c+n);
V[c+n].insert(b);
}
//最大匹配
int ans=0;
memset(match,-1,sizeof(match));
for(int i=1;i<n+m;i++){//注意初始时0已经开机
memset(vis,0,sizeof(vis));
if(dfs(i)) ans++;
}
cout<<ans/2<<endl;
}
return 0;
}
网络流算法(max-flow-min-cut)
构造残存网络
Ford-Fulkerson算法
算法思路:
推广:
代码(template):
#include<bits/stdc++.h>
using namespace std;
const int maxn=205;//最大结点数
const int inf=0x7fffffff;
int r[maxn][maxn]; //残留网络,初始化为原图
bool visit[maxn];//是否被访问过
int pre[maxn];//前驱子图,记录了从顶点s到终点的一条可行路径上的其它顶点的前一个顶点,从前驱子图中找到从s到t的一条完整路径
int m,n;//边数,顶点数
bool bfs(int s,int t) //广度优先寻找一条从s到t的增广路,若找到返回true
{
int p;
queue<int> q;//创建一个队列的对象
memset(pre,-1,sizeof(pre));//初始化前驱子图数组pre
memset(visit,false,sizeof(visit));//初始化访问数组
pre[s]=s;//让源点的前驱节点为本身
visit[s]=true;//标记访问
q.push(s);//把源点放进队列里面
while(!q.empty())//当队列不为空,继续找下一个结点
{
p=q.front();//取出队首
q.pop();
for(int i=1;i<=n;i++)
{
if(r[p][i]>0 && !visit[i])//如果从p出发到其他顶点,有残留容量大于0,并且没有被标记
{
pre[i]=p;//标记选出点的前驱节点为p
visit[i]=true;//标记访问
if(i==t) //如果找到汇点就结束
return true;
q.push(i);//接着将该点进栈
}
}
}
return false;
}
int EK(int s,int t)//计算最大流
{
int flow=0;
while(bfs(s,t))//当可以找到时
{
int d=inf;
for(int i=t;i!=s;i=pre[i])//从后往前找
d=d<r[pre[i]][i]? d:r[pre[i]][i];//找到增广路径中最小流量
for(int i=t;i!=s;i=pre[i])//从后往前更新
{
r[pre[i]][i]-=d;//正
r[i][pre[i]]+=d;//反
}
flow+=d;//流量++
}
return flow;
}
int main()
{
while(scanf("%d%d",&m,&n)!=EOF)
{
int u,v,w;
memset(r,0,sizeof(r));//初始化残留网络
for(int i=0;i<m;i++)
{
scanf("%d%d%d",&u,&v,&w);
r[u][v] = w;
}
printf("最大流:");
printf("%d\n",EK(1,n));
}
return 0;
}
相关例题
zoj1734 Power Network
#include<bits/stdc++.h>
using namespace std;
#define maxn 50000
#define MAX 120
int n,np,nc,m;//节点个数 生产点个数 消费点个数 中转点个数
int cap[MAX][MAX];//边容量
int EKarp(int s,int t)//s--源点 t---终点
{
queue<int> Q;//创建队列
int flow[MAX][MAX];//流量
int pre[MAX];//前导点
int node[MAX];//记录能够到index节点的最大流
int maxflow = 0;//初始化最大流为0
memset(flow,0,sizeof(flow));//初始化流量数组为0
while (true)
{
Q.push(s);//将起点放入队列
memset(node,0,sizeof(node));//初始化
node[s] = maxn;//初始化能够到起点的最大流
while(!Q.empty())
{
int u = Q.front();//取出队列第一个节点
Q.pop();
for (int v=0; v<=t; v++){//遍历所有点
if (!node[v] && cap[u][v]>flow[u][v])//如果v没有被访问过且能够正向流
{
Q.push(v);//假如邻接顶点
node[v] = min(node[u], cap[u][v]-flow[u][v]);//更新能够流到v的最大流
pre[v] = u;//标记v的前导节点为u
}
}
}
if (node[t] == 0) break;//若更新后流向终点的流量无增加,则说明已达到最大流
for (int u=t; u!=s; u=pre[u])//流量更新
{
flow[pre[u]][u] += node[t];
flow[u][pre[u]] -= node[t];
}
maxflow += node[t];//最大流更新
}
return maxflow;
}
int main()
{
int from,to,value;
while(scanf("%d%d%d%d",&n,&np,&nc,&m)!=EOF)
{
memset(cap,0,sizeof(cap));//初始化边容量
while (m--)
{
scanf(" (%d,%d)%d",&from,&to,&value);
cap[from][to] = value;
}
while (np--)
{
scanf(" (%d)%d",&from,&value);
cap[n][from] = value;//记起点index为 n
}
while (nc--)
{
scanf(" (%d)%d",&from,&value);
cap[from][n+1] = value;//记终点index为 n+1
}
printf("%d\n", EKarp(n,n+1));
}
return 0;
}
Dog 路径与点连边
#include <bits/stdc++.h>
using namespace std;
#define maxn 500
vector<int> V[maxn]; //存储图
bool vis[maxn]; //访问
int match[maxn]; //匹配
struct node
{
int x, y;
} nd[maxn];
double dis(node n1, node n2)
{
return sqrt(pow(n1.x - n2.x, 2) + pow(n1.y - n2.y, 2));
}
bool dfs(int u)
{
for (int i = 0; i < int(V[u].size()); i++)
{
int v = V[u][i]; //找出相邻结点
if (!vis[v])
{
int tmp1 = match[v]; //保存
match[v] = u;
int tmp2 = match[u];
match[u] = v;
vis[v] = true;
if (tmp1 == -1 || dfs(tmp1))
{
return true;
}
match[v] = tmp1;
match[u] = tmp2;
}
}
return false;
}
int main()
{
int n, m;
while (cin >> n >> m)
{
int cnt = 0;
for (int i = 0; i < n; i++)
{ //输入人的结点
cin >> nd[cnt].x >> nd[cnt].y; // cnt=0为起点,cnt=n-1为终点
cnt++;
}
for (int i = 0; i < m; i++)
{ //输入狗的结点
cin >> nd[cnt].x >> nd[cnt].y;
cnt++;
}
//建立图模型
//初始化
for (int i = 0; i < maxn; i++)
{
V[i].clear();
}
//人的每个相邻间隔标结点0~(n-2),狗结点(n-1)~(n+m-2)
//人的坐标0~(n-1),狗的坐标n~(n+m-1)
//连边
for (int i = n; i < n + m; i++)
{ //遍历所有狗结点
for (int j = 0; j < n - 1; j++)
{ //遍历所有人间隔
if (dis(nd[j], nd[j + 1]) * 2 >= dis(nd[j], nd[i]) + dis(nd[i], nd[j + 1]))
{ //满足条件,V[j]与V[i-1]连边
V[j].push_back(i - 1);
V[i - 1].push_back(j);
}
}
}
//求解最大匹配
memset(match, -1, sizeof(match));
int ans = 0;
for (int i = n - 2; i >= 0; i--)
{
memset(vis, 0, sizeof(vis));
if (dfs(i))
{
ans++;
}
}
cout << n + ans << endl;
for (int i = 0; i < n - 1; i++)
{
cout << nd[i].x << " " << nd[i].y << " ";
if (match[i] != -1)
{
cout << nd[match[i] + 1].x << " " << nd[match[i] + 1].y << " ";
}
}
cout << nd[n - 1].x << " " << nd[n - 1].y;
}
return 0;
}