网络流总结

坚强是说给别人听的谎言 提交于 2019-11-27 03:21:30

洛谷P2756 飞行员配对方案问题

分析

其实就是一个二分图匹配求最大匹配数的问题,加一个源点和汇点,再跑一遍网络流,输出方案的时候检查一下有没有流经过即可(反向边是否非0)。

注意:每个点在向源点和汇点连边时,要在输入完之后再连。否则会重复连。

#include<bits/stdc++.h>
using namespace std;
#define N 205
#define M 30005
int to[M],head[N],nex[M],w[M],lev[N],tot=1,cnt=0,inf=0x7f7f7f,s=0,t=101,n,cur[N];
struct node{ int u,v,bian; }e[M];
queue<int> q;
void add(int a,int b,int ww)
{
    tot++; to[tot]=b; w[tot]=ww; nex[tot]=head[a]; head[a]=tot;
    if(ww==0&&b&&a!=t) e[cnt].bian=tot;
}
bool bfs()
{
    memset(lev,-1,sizeof(lev));
    lev[s]=0; q.push(s);
    while(!q.empty())
    {
        int u=q.front(); q.pop();
        for(int i=head[u];i!=-1;i=nex[i])
        {
            int v=to[i];
            if(w[i]>0&&lev[v]==-1)
            lev[v]=lev[u]+1,q.push(v);
        }
    }
    if(lev[t]!=-1) return true;
    return false; 
}
int dfs(int u,int flow)
{
    if(u==t) return flow;
    int ret=flow;
    for(int i=cur[u];i!=-1;i=nex[i])
    {
        cur[u]=i;
        if(ret<=0) break;
        int v=to[i];
        if(w[i]>0&&lev[u]+1==lev[v])
        {
            int k=dfs(v,min(w[i],ret));
            ret-=k; w[i]-=k; w[i^1]+=k; 
            //异或运算:2k^1=(2k+1) (2k+1)^1=2k;
        }
    }
    return flow-ret;//最大限制 - 还剩的多少没流 =流出去了多少 
}
void dinic()
{
    int ans=0;
    while(bfs()){
        for(int i=0;i<=101;i++) cur[i]=head[i]; 
        ans+=dfs(s,inf);
    }
    printf("%d\n",ans);
}
int main()
{
    int m,a,b;
    scanf("%d%d",&m,&n);
    memset(head,-1,sizeof(head));
    while(1){
        scanf("%d%d",&a,&b);
        if(a==-1&&b==-1) break;
        cnt++;
        add(a,b,inf); add(b,a,0); e[cnt].u=a; e[cnt].v=b;
    }
    //一定要在后面建,否则会重复建边 
    for(int i=1;i<=m;i++) add(s,i,1),add(i,s,0);
    for(int i=m+1;i<=n;i++) add(i,t,1),add(t,i,0);
    dinic();
    for(int i=1;i<=cnt;i++)
    if(w[e[i].bian]!=0) printf("%d %d\n",e[i].u,e[i].v);
}
/*
5 10
1 7
1 8
2 6
2 9
2 10
3 7
3 8
4 7
4 8
5 10
-1 -1

2 4
1 3
1 4
2 3
-1 -1
*/
View Code

 

洛谷P2765 魔术球问题

«问题描述:

假设有n根柱子,现要按下述规则在这n根柱子中依次放入编号为1,2,3,...的球。

(1)每次只能在某根柱子的最上面放球。

(2)在同一根柱子中,任何2个相邻球的编号之和为完全平方数。

试设计一个算法,计算出在n根柱子上最多能放多少个球。例如,在4 根柱子上最多可放11 个球。

n<=55

分析

首先,这道题的主要限制在于球(平方数),考虑对球建边。将每个球拆成两部分,第一部分对 s 连边,第二部分对 t 连边, 两部分间通过和为平方数的条件连边(边权都为1)。一个一个地加球,加一次跑一次最大流,如果在以前的基础上产生了新流,说明加入的这个球能对已有的球产生平方和的联系,即可以加在某一个球上面。加球一直到没有产生新流了,说明需要加柱子了,这时就将柱子++,一直到达到n个为止。

方案输出

每当新加一个柱子时,记录最先放入柱子的球,然后在找增广路的时候对每一个点,记录下一个可行的点是谁,类似于邻接表一样的链表式结构。然后输出的时候枚举柱子。

#include<bits/stdc++.h>
using namespace std;
#define inf 2100000000
#define M 1000005
int to[M],nex[M],head[M],w[M],tot=0,vis[M];
int nexxt[M],headd[M],lev[M],cur[M],s=M-5,t=M-4;
queue<int> q;
void add(int a,int b,int ww)
{
    to[++tot]=b; nex[tot]=head[a]; head[a]=tot; w[tot]=ww;
    to[++tot]=a; nex[tot]=head[b]; head[b]=tot; w[tot]=0;
}
bool bfs()
{
    memset(lev,-1,sizeof(lev));
    lev[s]=0; q.push(s);
    while(!q.empty()){
        int u=q.front(); q.pop();
        for(int i=head[u];i!=-1;i=nex[i]){
            int v=to[i];
            if(lev[v]==-1 && w[i]) lev[v]=lev[u]+1,q.push(v);
        }
    }
    return lev[t]!=-1;
}
int dfs(int u,int flow)
{
    if(u==t) return flow;
    int ret=flow;
    for(int i=head[u];i!=-1;i=nex[i]){
        int v=to[i];
        if(ret<=0) break;
        if(lev[v]==lev[u]+1&&w[i]>0){
            int k=dfs(v,min(ret,w[i]));
            nexxt[u>>1]=v>>1;
            //记录一下这个球对应的下一个球是哪一个 因为存的时候*2+1了 所以要/2 不需要分奇偶讨论 
            ret-=k; w[i]-=k; w[i^1]+=k;
        }
    } 
    return flow-ret;
}
int dinic()
{
    int ans=0;
    while(bfs()){
        ans+=dfs(s,inf);
    } 
    return ans;
}
int main()
{
    int n;
    scanf("%d",&n);
    memset(head,-1,sizeof(head));
    int now=0,zhu=0;//
    headd[1]=1;
    while(zhu<=n){
        now++;
        add(s,now<<1,1); add(now<<1|1,t,1);
        for(int i=sqrt(now)+1;i*i<now*2;i++)
         add((i*i-now)<<1,now<<1|1,1);
        int tmp=dinic();
        if(!tmp) headd[++zhu]=now;
    }
    printf("%d\n",now-1);
    for(int i=1;i<=n;i++)
    if(!vis[headd[i]]){
        for(int j=headd[i];j&&j!=t>>1;j=nexxt[j])
        printf("%d ",j),vis[j]=true;
        printf("\n");
    }
}
View Code

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!