网络流+二分图模板

  1. 最大流

//是否拆点慎重考虑,如果是无向边,就不加0的反边了

//最小割即为最大流,拆点,其他边权为0x3f3f3f3f,点与点’之间为点值,然后求最大流就可,//注意是否无向,是否需要加方向

例:POJ 1087 A Plug for UNIX

#include<bits/stdc++.h>//最大流模板
#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()
{
queue<int>Q;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(x<l) {l=x;start=i;}
            if(x>r) {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;
}
   2. 最小费用流

   Minimum Cost 

#include<bits/stdc++.h>
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<Edge> 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<int> 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<all) cost=-1;
        return cost;
    }
}MM;
int main()
{
    int n,m,i,j,z,k,x,l,ans,ma[105][105],ma1[105][105],need[105];
    while(~scanf("%d%d%d",&n,&m,&k))
    {
            MM.init(m+n+1,0,m+n+1);
            memset(head,-1,sizeof(head));
            //输入,存图
       printf("%d\n",MM.Min_cost(need[l]));
    }
    return 0;
}

 

   3.二分图常见模型

  1. 二分图最大匹配  (网络流做法)
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++;
    }
}

   2.最小点覆盖        

(二分图的最小覆盖=二分图的最大匹配)

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<m; j++)
    {
        if((!vis[j])&&map[i][j])
        {
            vis[j]=1;
            if(num[j]<mid)//n个人,m组,最多的组不超过mid个人
            {
                link[j][num[j]++]=i;
                return 1;
            }
            for(int p=0; p<num[j]; p++)
                if(dfs(link[j][p]))
                {
                    link[j][p]=i;
                    return 1;
                }
        }
    }
    return 0;
}
int mult()
{
    memset(num,0,sizeof(num));
    for(int i=0; i<n; i++)
    {
        memset(vis,0,sizeof(vis));
        if(!dfs(i))
            return 0;
    }
    return 1;
} 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值