//是否拆点慎重考虑,如果是无向边,就不加0的反边了
//最小割即为最大流,拆点,其他边权为0x3f3f3f3f,点与点’之间为点值,然后求最大流就可,//注意是否无向,是否需要加方向
例:POJ 1087 A Plug for UNIX
#include//最大流模板
#define maxn 210000
#define INF 200010
using namespace std;
int des,N,D,F,start,n,m,head[maxn],num,dist[maxn],vis[maxn],cur[maxn];
struct Edge
{
int u,v,cap,flow,next;
Edge(int x,int y,int ca,int fl,int ne)
:u(x),v(y),cap(ca),flow(fl),next(ne){}
Edge(){}
}edge[INF];
void addedge(int u,int v,int x)
{
Edge E(u,v,x,0,head[u]);edge[num]=E; head[u]=num++;
Edge E1(v,u,0,0,head[v]);edge[num]=E1;head[v]=num++;
}
bool bfs()
{
queueQ;memset(dist,-1,sizeof(dist));memset(vis,0,sizeof(vis));
while(!Q.empty())Q.pop();
Q.push(start);dist[start]=0;vis[start]=1;
while(!Q.empty()){
int u=Q.front();Q.pop();
for(int i=head[u];i!=-1;i=edge[i].next)
{
Edge E=edge[i];
if(!vis[E.v]&&E.cap>E.flow)
{
vis[E.v]=1;
dist[E.v]=dist[u]+1;
if(E.v==des) return true;
Q.push(E.v);
}
}
}
return false;
}
int dfs(int u,int a)
{
if(u==des||a==0) return a;
int flow=0,f;
for(int& i=cur[u];i!=-1;i=edge[i].next)
{
Edge& E=edge[i];
if(dist[E.v]==dist[u]+1&&(f=dfs(E.v,min(a,E.cap-E.flow)))>0)
{
E.flow+=f;
edge[i^1].flow-=f;
flow+=f;
a-=f;
if(a==0) break;
}
}
return flow;
}
int Dinic()
{
int res=0;
while(bfs())
{
memcpy(cur,head,sizeof(head));
res+=dfs(start,INF);
}
return res;
}
void getmap()
{
int x,i,j,z,t,l=INF,r=-INF,y;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
{
scanf("%d%d",&x,&y);
if(xr) {r=x;des=i;}
}
for(i=1;i<=m;i++)
{
scanf("%d%d%d",&x,&y,&z);
addedge(x,y,z);
//addedge(y,x,z);
}
return;
}
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
memset(head,-1,sizeof(head));
num=0;
getmap();
printf("%d\n",Dinic());
}
return 0;
}
Minimum Cost
#include
using namespace std;
#define INF 0x3f3f3f3f
const int maxn=200+10;
int head[maxn];
struct Edge
{
int from,to,cap,flow,cost,next;
Edge(){}
Edge(int f,int t,int c,int fl,int co,int ne):
from(f),to(t),cap(c),flow(fl),cost(co),next(ne){}
};
struct MCMF
{
int n,m,s,t;
vector edges;
bool inq[maxn]; //是否在队列
int d[maxn]; //Bellman_ford单源最短路径
int p[maxn]; //p[i]表从s到i的最小费用路径上的最后一条弧编号
int a[maxn]; //a[i]表示从s到i的最小残量
//初始化
void init(int n,int s,int t)
{
this->n=n, this->s=s, this->t=t;
edges.clear();
}
//添加一条有向边
void AddEdge(int from,int to,int cap,int cost)
{
edges.push_back(Edge(from,to,cap,0,cost,head[from]));
edges.push_back(Edge(to,from,0,0,-cost,head[to]));
m=edges.size();
head[from]=m-2;
head[to]=m-1;
}
//求一次增广路
bool BellmanFord(int &flow, int &cost)
{
for(int i=0;i<=n;++i) d[i]=INF;
memset(inq,0,sizeof(inq));
d[s]=0, a[s]=INF, inq[s]=true, p[s]=0;
queue Q;
Q.push(s);
while(!Q.empty())
{
int u=Q.front(); Q.pop();
inq[u]=false;
for(int i=head[u];i!=-1;i=edges[i].next)
{
Edge &e=edges[i];
if(e.cap>e.flow && d[e.to]>d[u]+e.cost)
{
d[e.to]=d[u]+e.cost;
p[e.to]=i;
a[e.to]= min(a[u],e.cap-e.flow);
if(!inq[e.to]){ Q.push(e.to); inq[e.to]=true; }
}
}
}
if(d[t] ==INF) return false;
flow+=a[t];
cost+=a[t]*d[t];
int u=t;
while(u!=s)
{
edges[p[u]].flow+=a[t];
edges[p[u]^1].flow-=a[t];
u=edges[p[u]].from;
}
return true;
}
//求出最小费用最大流
int Min_cost(int all)
{
int flow=0,cost=0;
while(BellmanFord(flow,cost));
if(flow
3.二分图常见模型
bool used[110];
int p[110];
bool MAP[1010][1010];
int num,n,m,len,t,v;
bool work(int c)
{
for(int i = 1; i <= m; i++)
{
if(!used[i] && MAP[c][i])
{
used[i] = true;
if(p[i]==-1 || work(p[i]))
{
p[i] = c;
return true;
}
}
}
return false;
}
void sol()
{
num=0;
for(int i = 1; i <= n; i++)
{
memset(used,0,sizeof(used));
if(work(i))
num++;
}
}
(二分图的最小覆盖=二分图的最大匹配)
POJ 3041 Asteriods
给一个N*N的矩阵,有些格子有障碍,要求我们消除这些障碍,问每次消除一行或一列的障
碍,最少要几次。
矩阵上每一个点只需要选中一次,那么对应从行部连到列部的一条边,把所有矩阵上的点消除就需要用最少的点来覆盖这些边,所以匈牙利跑一边即可。
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn = 1005;
struct Edge
{
int to,next;
}edge[maxn*maxn];
int head[maxn];
int maxedge;
inline void addedge(int u,int v)
{
edge[++maxedge] = (Edge) { v,head[u] };
head[u] = maxedge;
}
int r,c,n;
bool init()
{
if(!~scanf("%d%d%d",&r,&c,&n) || !(r|c|n)) return false;
memset(head,-1,sizeof(head));
maxedge = -1;
for(int i=1;i<=n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
addedge(x,y);
}
return true;
}
bool S[maxn],T[maxn];
int matchx[maxn],matchy[maxn];
bool dfs(int u)
{
S[u]=true;
for(int i=head[u];~i;i=edge[i].next)
{
int v = edge[i].to;
if(T[v]) continue;
T[v]=true;
if(!matchy[v] || dfs(matchy[v]))
{
matchx[u]=v;
matchy[v]=u;
return true;
}
}
return false;
}
int hungary()
{
int ret=0;
memset(matchx,0,sizeof(matchx));
memset(matchy,0,sizeof(matchy));
for(int i=1;i<=r;i++)
{
memset(S,0,sizeof(S));
memset(T,0,sizeof(T));
if(dfs(i)) ret++;
}
return ret;
}
void work()
{
int ans = hungary();
printf("%d",ans);
memset(S,0,sizeof(S));
memset(T,0,sizeof(T));
for(int i=1;i<=r;i++) if(!matchx[i])
dfs(i);
for(int i=1;i<=r;i++) if(!S[i])
printf(" r%d",i);
for(int i=1;i<=c;i++) if(T[i])
pr
intf(" c%d",i);
putchar('\n');
}
int main()
{
while(init())
work();
return 0;
}
3.最小边覆盖(最小路径覆盖= 节点数-最大匹配)
马控制棋盘问题
已知n * m的棋盘,其中一些格子有障碍物。现要在棋盘上无障碍物的格子中布置一些马,每只马可以控制两个格子:一个是它所在的格子,另一个是它通过一步可以跳到的格子。至少要放多少只马才能将所有无障碍物的格子控制?
对于这道题,可以先将棋盘染色 那么发现马只能连接不同颜色的两个格子,那么把需要的边建好之后,求出最小的边使得所有非障碍点都被守住即可。
4.最大独立集 (最大独立集 = 节点数-最大匹配)
在总的点集中,去掉最少的点,使得剩下的点相互之间没有边。用最少的点去覆盖所有的边,也就是最小覆盖。
5.二分图的必要边
将点和图进行匹配,求出最大匹配,再进行删边匹配,若匹配数不再为n,则说明这条边为必要边
6.多重匹配 (可结合二分,最小的最大等)
bool dfs(int i)
{
for(int j=0; j