网络流——刷题记录

POJ 3041 Asteroids

链接
简述:
N*N的坐标系,有K颗行星,一次可以消灭一行或者一列行星,最少需要几次?
方案:
以横坐标方向和纵坐标方向的坐标为点,建立顶点两个顶点集,行星(x,y)则变成由横坐标点x指向纵坐标y的点,进行一个二分图的建立
在这里插入图片描述

二分图中,最小顶点覆盖等于最大匹配

Konig定理:二分图中,最大匹配数等于最小点覆盖数。
最小点覆盖:每个点覆盖以它为端点的边,选择最少的点来覆盖所有边。

因此问题可以变成一个二分图匹配问题。
最后使用网络流求解。
代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cmath>
#include<queue>
#include<vector>
#include<map>

#define fast ios::sync_with_stdio(false), cin.tie(0), cout.tie(0)
#define ll long long
#define endl "\n"
#define pii pair<int,int>
#define pb push_back
#define debug(x) cout << "visit:" << x << endl
#define inf 2147483647

using namespace std;

//白书的dinic板子 
struct edge
{
	int to, cap, rev;
};

const int MAX_V = 1e3+5;

vector<edge> G[MAX_V];
int level[MAX_V];
int iter[MAX_V];

void add_edge(int from,int to,int cap)
{
	G[from].push_back( (edge){to,cap,G[to].size()});
	G[to].push_back( (edge){from,0,G[from].size()-1});
}

//标记距离 
void bfs(int s)
{
	for(int i=0;i<MAX_V;++i)
	{
		level[i] = -1;
	}
	queue<int> que;
	level[s] = 0;
	que.push(s);
	while(que.size()>0)
	{
		int v = que.front();
		que.pop();
		for(int i=0;i<G[v].size();++i)
		{
			edge &e = G[v][i];
			if(e.cap>0&&level[e.to]<0)
			{
				level[e.to] = level[v]+1;
				que.push(e.to);
			}
		}
	}
}

//寻找增广路 
int dfs(int v,int t,int f)
{
	if(v==t)
	{
		return f;
	}
	for(int &i=iter[v];i<G[v].size();++i)
	{
		edge &e = G[v][i];
		if(e.cap>0&&level[v]<level[e.to])
		{
			int d = dfs(e.to,t,min(f,e.cap));
			if(d>0)
			{
				e.cap -= d;
				G[e.to][e.rev].cap += d;
				return d;
			}
			
		}
	}
	return 0;
}

int max_flow(int s,int t)
{
	int flow = 0;
	while(1)
	{
		bfs(s);
		if(level[t]<0)//不再可达,说明已经是最大流 
		{
			return flow;
		}
		for(int i=0;i<MAX_V;++i)
		{
			iter[i] = 0;
		}
		int f;
		while((f=dfs(s,t,inf))>0)
		{
			flow += f;
		}
	}
}

int main()
{
	int s, t;
	int n, k;
	scanf("%d %d",&n,&k);
	s = 0;
	t = 2*n+1;
	for(int i=1;i<=n;++i)
	{
		add_edge(s,i,1);
	}
	for(int i=1;i<=n;++i)
	{
		add_edge(i+n,t,1);
	}
	for(int i=1;i<=k;++i)
	{
		int x, y;
		scanf("%d %d",&x,&y);
		add_edge(x,y+n,1);
	}
	printf("%d\n",max_flow(s,t));
	return 0;
}

POJ 3281 Dining

链接
简述
有n头牛,f种食物,d种饮品,每头牛喜欢不同的食物饮品,1种食物或饮品只能被分配给1头牛,怎么分配,使最多的牛获得心仪的食物和饮品。
方案
这个属于是一眼网络流的题,很容易想到要用二分图最大匹配去求解,但比较需要注意的有两点:
一是,有食物和饮品需要去做匹配,所以需要将两个匹配结合起来去解决问题。
二是,牛代表的点需要拆点,中间用一条容量为1 的边来表示。,这样是为了保证多种饮食供给给同一头牛时,只算做满足了一头牛
直接上图:
另:下图所有边的容量默认为1
在这里插入图片描述
在这里插入图片描述
代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<string>
#include<cmath>
#include<queue>
#include<vector>
#include<map>

using namespace std;

#define ll long long
#define fast ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
#define de(x) cout <<':' << x << endl
#define inf 2e9

//白书的dinic板子 
struct edge
{
	int to, cap, rev;
};

const int MAX_V = 1e3+5;

vector<edge> G[MAX_V];
int level[MAX_V];
int iter[MAX_V];

void add_edge(int from,int to,int cap)
{
	G[from].push_back( (edge){to,cap,G[to].size()});
	G[to].push_back( (edge){from,0,G[from].size()-1});
}

//标记距离 
void bfs(int s)
{
	for(int i=0;i<MAX_V;++i)
	{
		level[i] = -1;
	}
	queue<int> que;
	level[s] = 0;
	que.push(s);
	while(que.size()>0)
	{
		int v = que.front();
		que.pop();
		for(int i=0;i<G[v].size();++i)
		{
			edge &e = G[v][i];
			if(e.cap>0&&level[e.to]<0)
			{
				level[e.to] = level[v]+1;
				que.push(e.to);
			}
		}
	}
}

//寻找增广路 
int dfs(int v,int t,int f)
{
	if(v==t)
	{
		return f;
	}
	for(int &i=iter[v];i<G[v].size();++i)
	{
		edge &e = G[v][i];
		if(e.cap>0&&level[v]<level[e.to])
		{
			int d = dfs(e.to,t,min(f,e.cap));
			if(d>0)
			{
				e.cap -= d;
				G[e.to][e.rev].cap += d;
				return d;
			}
			
		}
	}
	return 0;
}

int max_flow(int s,int t)
{
	int flow = 0;
	while(1)
	{
		bfs(s);
		if(level[t]<0)//不再可达,说明已经是最大流 
		{
			return flow;
		}
		for(int i=0;i<MAX_V;++i)
		{
			iter[i] = 0;
		}
		int f;
		while((f=dfs(s,t,inf))>0)
		{
			flow += f;
		}
	}
}

int main()
{
    int n, f, d;
    scanf("%d %d %d",&n,&f,&d);
    int s = 0, t = f+2*n+d+1;
    for(int i=1;i<=f;++i)
    {
        add_edge(s,i,1);
    }
    for(int i=1;i<=d;++i)
    {
        add_edge(f+2*n+i,t,1);
    }
    for(int i=1;i<=n;++i)
    {
        add_edge(f+i,f+n+i,1);
    }
    for(int i=1;i<=n;++i)
    {
        int F, D;
        scanf("%d %d",&F,&D);
        for(int j=1;j<=F;++j)
        {
            int fn;
            scanf("%d",&fn);
            add_edge(fn,f+i,1);
        }
        for(int j=1;j<=D;++j)
        {
            int dn;
            scanf("%d",&dn);
            add_edge(f+n+i,f+2*n+dn,1);
        }
    }
    printf("%d\n",max_flow(s,t));
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值