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;
}