ACM网络流学习

练习题目:点击打开链接  网络流建模:点击打开链接

之前写过 关于网络流的算法入门,其实那么多会一个就OK.首选Dinic,递归很好写25行.

邻接表时候若是无向图则是四条边

解决和值问题,都并入汇点

找多条不同的路径,最小费用流问题.

J 模板题

测试Dinic模板

K 电脑公司,

这个其实也可以不用拆点,不过拆点更容易想,这个是第一个需要构图的题目,完全不会.其实就是不同类型机器之间连接起来输入输出看好就行

N~N+x 为拆后点,之间的cap为工作量

#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
#define MAXN 200
int G[MAXN][MAXN];
int G2[MAXN][MAXN];
int pre[MAXN][3];
int level[MAXN];
queue<int> Q;
int sp, tp;
int N,M;
struct P{
    int w,in[11],out[11];
}pnode[MAXN];
void _DEBUG(){
    for(int i=0;i<=N;i++){
        for(int j=1;j<=N;j++)
            printf("%d ",G[i][j]);
        printf("\n");
    }
}
int BFS(){
    memset(level,-1,sizeof(level));
    Q.push(sp);level[sp]=0;
    while(Q.size()){
        int u=Q.front();Q.pop();
        for(int v=1;v<=N;v++) if(G[u][v] && level[v]==-1){
            level[v]=level[u]+1;
            Q.push(v);
        }
    }
    return level[tp]!=-1;
}
int extendPath(int u,int beforemin){
    int res=0,t;
    if(u==tp || !beforemin) return beforemin;
    for(int v=1;v<=N;v++)///u,v联通 v是下一层 获得扩展路径 四个括号
        if(G[u][v] && level[v]==level[u]+1 && (t=extendPath(v,min(beforemin,G[u][v])))){
            G[u][v]-=t;
            G[v][u]+=t;
            res+=t;
            beforemin-=t;
            if(!beforemin) break;
    }
    if(!res) level[u]=-1;///一个常数优化,该点不能再增加流了
    return res;
}
/**
将一个机器拆点 另一个点是i+N
对于输出的每台机器的连接情况
将原图保存到G2,比对就好了.所有的枚举都是i+N->j 出边到入边
**/
void createG(){
    for(int i=1;i<=N;i++){
        int flag=1;
        G[i][i+N]=pnode[i].w;
        for(int j=0;j<M && flag;j++)
            if(pnode[i].in[j]==1) flag=0;///构图要注意 全为0或2都行
        if(flag)
           G[sp][i]=inf;
        flag=1;
        for(int j=0;j<M && flag;j++)
            if(pnode[i].out[j]==0) flag=0;
        if(flag)
           G[i+N][tp]=inf;
        for(int j=1;j<=N;j++) if(i!=j) {
            flag=1;
            for(int k=0;k<M;k++)
                if(pnode[i].out[k] + pnode[j].in[k]==1) flag=0;
            if(flag)
                G[i+N][j]=inf;
        }
    }
}
int main( ){
   while(~scanf( "%d%d",&M,&N )){
        for(int i=1;i<=N;i++){
            scanf("%d",&pnode[i].w);
            for(int j=0;j<M;j++)
                scanf("%d",&pnode[i].in[j]);
            for(int j=0;j<M;j++)
                scanf("%d",&pnode[i].out[j]);
        }
        memset(G,0,sizeof(G));
        sp=0;tp=2*N+1;int ans=0,tans;
        createG();
        memcpy(G2,G,sizeof(G));
        N=tp;///_DEBUG();
        while(BFS())
            while(tans=extendPath(sp,inf))
                ans+=tans;
        int num=0;N/=2;
        for(int i=N+1;i<=2*N;i++)
            for(int j=1;j<=N;j++)
                if(i!=j+N && G2[i][j]!=G[i][j]){
                        pre[num][0]=i-N;
                        pre[num][1]=j;
                        pre[num][2]=G2[i][j]-G[i][j];
                        num++;
                }
        printf("%d %d\n",ans,num);
        for(int i=0;i<num;i++)
            printf("%d %d %d\n",pre[i][0],pre[i][1],pre[i][2]);
    }
   return 0;
}

L 求奶牛和挤奶器匹配前提下,奶牛走的路程最短.

这道题难点在于读懂,看明白了就好搞,其限制是奶牛和挤奶器连边的条件    .二分法很快

M 神题啊,客户去猪,然后将猪圈里的猪重新分配下,使得可以获得的猪数量最多.换个角度,猪圈的猪实际上就是第一个可以接触的人重新分配,后面的人通过他

拿猪.规约不好想.点击打开链接

M 通过矩阵的每行和,每列和,每个元的限制条件得出矩阵.

这里矩阵的每个元素是通过点之间的流量表明的,即边.刚开始将所有的点都纳入,忒多了.

源点连每行和,每列和连汇点.

F 裸 不过可以用贪心去做,策略:使得后面上界大的有更多选择.

/**
题解:
贪心即可。
先将翻晒霜按spf值排序,从小到大处理。
对于当前处理的防晒霜,找到能使用它且spf上界最小的牛,将防晒霜供其使用。
因为上界越大,选择越多,在之后可能也可以找到匹配的防晒霜,
而由于从小到大处理,下界已经没有意义(不可能找到比当前spf更小的匹配),这是贪心的原则。
复杂度:O(l*logl+n*l)
转载自http://blog.163.com/benz_/blog/static/18684203020115612011262/
使得后面有更多的选择 eg 教室排时间
**/
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
const int MAXN=5010;
int N,M,T;

int level[MAXN];
int vis[MAXN];
struct P{
    int u,v,cap,cost,nxt;
}node[2000005];int tot,head[MAXN];
void _DEBUG(int N,int (*G)[MAXN]){
    printf("\n");
    for(int i=0;i<=N;i++){
        printf("\n");
        for(int j=0;j<=N;j++)
            printf("%5d ",G[i][j]);
    }printf("\n");
}
bool bfs(int sp,int tp,int NN){
    memset(level,-1,sizeof(level));
    queue<int> Q;
    Q.push(sp);
    level[sp]=0;
    while(Q.size()){
        int u=Q.front();Q.pop();
        for(int i=head[u];~i;i=node[i].nxt) if(node[i].cap>0 && level[node[i].v]==-1){
            Q.push(node[i].v);
            level[node[i].v]=level[u]+1;
        }
    }
    return level[tp]!=-1;
}
int extendPath(int sp,int tp,int NN,int u,int beforemin){
    int res=0,t;
    if(u==tp || beforemin==0) return beforemin;
    for(int i=head[u];~i;i=node[i].nxt)
        if(node[i].cap>0 && level[node[i].v]==level[u]+1 && (t=extendPath(sp,tp,NN,node[i].v,min(beforemin,node[i].cap)))){
            node[i].cap-=t;
            node[i^1].cap+=t;
            beforemin-=t;
            res+=t;
            if(!beforemin) break;
    }
    if(!res) level[u]=-1;
    return res;
}

int pre[MAXN];
int dist[MAXN];
int spfa(int sp,int tp,int NN){
    queue<int> Q;
    memset(vis,0,sizeof(vis));
    memset(dist,0x3f,sizeof(dist));
    dist[sp]=0;vis[sp]=1;pre[sp]=-1;
    Q.push(sp);
    while(Q.size()){
        int u=Q.front();Q.pop();vis[u]=0;
        for(int i=head[u];~i;i=node[i].nxt) {
            int v=node[i].v;
            if(node[i].cap>0 && dist[v]>dist[u]+node[i].cost){
                dist[v]=dist[u]+node[i].cost;
                pre[v]=i;
                if(!vis[v])
                    vis[v]=1,Q.push(v);
                }
        }
    }
    return dist[tp]!=inf;
}

int mcmf(int sp,int tp,int NN){
    int ans=0,flow_sum=0;
    while(spfa(sp,tp,NN)){
        int t=inf;
        for(int i=pre[tp];i!=-1;i=pre[node[i].u])
            t=min(t,node[i].cap);
        for(int i=pre[tp];i!=-1;i=pre[node[i].u]){
            node[i].cap-=t;
            node[i^1].cap+=t;
        }ans+= dist[tp]*t;flow_sum+=t;
    }
    return ans;
}
void add(int a,int b,int c,int d){
    node[tot].u=a;node[tot].v=b;
    node[tot].cap=c;node[tot].cost=d;
    node[tot].nxt=head[a];head[a]=tot++;

    node[tot].u=b;node[tot].v=a;
    node[tot].cap=0;node[tot].cost=-d;
    node[tot].nxt=head[b];head[b]=tot++;
}
struct COW{
    int spf1,spf2,id;
}SPF[MAXN];
bool cmp(const struct COW &a,const struct COW &b){
    return a.spf1<b.spf1;
}
int main(){
    while(~scanf("%d%d",&N,&M)){
        tot=0;
        memset(head,-1,sizeof(head));
        int sp=0,tp=N+M+1;
        for(int i=1;i<=N;i++)
            add(sp,i,1,0),scanf("%d%d",&SPF[i].spf1,&SPF[i].spf2),SPF[i].id=i;
        ///sort(SPF+1,SPF+1+N,cmp);
        for(int i=1;i<=M;i++){
            int t,k;
            scanf("%d%d",&t,&k);
            add(i+N,tp,k,0);
            for(int j=1;j<=N;j++){
                if(t>=SPF[j].spf1 && t<=SPF[j].spf2){
                /*    if(i==608 && j==105)
                        k-=2;*/
                    add(SPF[j].id,i+N,1,0); /// SPF[i].id   这里写错了浪费时间 node 太少了无限RE
                }
            }
        }
        int ans=0,tans;
        while(bfs(sp,tp,tp+1))
            while(tans=extendPath(sp,tp,tp+1,sp,inf))
                ans+=tans;
        printf("%d\n",ans);
    }
    return 0;
}

A 将350天都当做结点,起初我只是将7天当做了结点.源点连接电影为D,电影连接7天为week,7天连汇点为可连接到汇点电影中的最大值.

这样就忽视了时间顺序这个条件.

/**
起初以为79个点就够了,源点到电影 cap=day,电影到7天 cap=week,7天到汇点为最大week
没有考虑时间先后关系,即条件变宽了,eg两个电影都要求2week完成但是第三个电影宽限
为4week,借着这条路就也能完成要求

**/
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
const int MAXN=410;
int N,M,T;
int sp,tp;
int level[MAXN];
int vis[MAXN];
struct P{
    int u,v,cap,cost,nxt;
}node[MAXN*MAXN];int tot,head[MAXN];
void _DEBUG(int N,int (*G)[MAXN]){
    printf("\n");
    for(int i=0;i<=N;i++){
        printf("\n");
        for(int j=0;j<=N;j++)
            printf("%5d ",G[i][j]);
    }printf("\n");
}
bool bfs(int sp,int tp,int NN){
    memset(level,-1,sizeof(level));
    queue<int> Q;
    Q.push(sp);
    level[sp]=0;
    while(Q.size()){
        int u=Q.front();Q.pop();
        for(int i=head[u];~i;i=node[i].nxt) if(node[i].cap>0 && level[node[i].v]==-1){
            Q.push(node[i].v);
            level[node[i].v]=level[u]+1;
        }
    }
    return level[tp]!=-1;
}
int extendPath(int sp,int tp,int NN,int u,int beforemin){
    int res=0,t;
    if(u==tp || beforemin==0) return beforemin;
    for(int i=head[u];~i;i=node[i].nxt)
        if(node[i].cap>0 && level[node[i].v]==level[u]+1 && (t=extendPath(sp,tp,NN,node[i].v,min(beforemin,node[i].cap)))){
            node[i].cap-=t;
            node[i^1].cap+=t;
            beforemin-=t;
            res+=t;
            if(!beforemin) break;
    }
    if(!res) level[u]=-1;
    return res;
}

int pre[MAXN];
int dist[MAXN];
int spfa(int sp,int tp,int NN){
    queue<int> Q;
    memset(vis,0,sizeof(vis));
    memset(dist,0x3f,sizeof(dist));
    dist[sp]=0;vis[sp]=1;pre[sp]=-1;
    Q.push(sp);
    while(Q.size()){
        int u=Q.front();Q.pop();vis[u]=0;
        for(int i=head[u];~i;i=node[i].nxt) {
            int v=node[i].v;
            if(node[i].cap>0 && dist[v]>dist[u]+node[i].cost){
                dist[v]=dist[u]+node[i].cost;
                pre[v]=i;
                if(!vis[v])
                    vis[v]=1,Q.push(v);
                }
        }
    }
    return dist[tp]!=inf;
}

int mcmf(int sp,int tp,int NN){
    int ans=0,flow_sum=0;
    while(spfa(sp,tp,NN)){
        int t=inf;
        for(int i=pre[tp];i!=-1;i=pre[node[i].u])
            t=min(t,node[i].cap);
        for(int i=pre[tp];i!=-1;i=pre[node[i].u]){
            node[i].cap-=t;
            node[i^1].cap+=t;
        }ans+= dist[tp]*t;flow_sum+=t;
    }
    return ans;
}
void add(int a,int b,int c,int d){
    node[tot].u=a;node[tot].v=b;
    node[tot].cap=c;node[tot].cost=d;
    node[tot].nxt=head[a];head[a]=tot++;

    node[tot].u=b;node[tot].v=a;
    node[tot].cap=0;node[tot].cost=-d;
    node[tot].nxt=head[b];head[b]=tot++;
}
int in[MAXN][10];
int main(){
    for(scanf("%d",&T);T;T--){
        scanf("%d",&N);
        tot=0;sp=0;tp=371;
        memset(head,-1,sizeof(head));
        for(int i=1;i<=350;i++) add(i,371,1,0);
        int tmp=0;
        for(int i=1;i<=N;i++)
            for(int j=1;j<=9;j++) scanf("%d",in[i]+j);
        for(int i=1;i<=N;i++){
            add(sp,350+i,in[i][8],0);
            tmp+=in[i][8];
            for(int j=1;j<=7;j++) if(in[i][j]==1)
                for(int k=0;k<in[i][9];k++)
                    add(350+i,k*7+j,1,0);
        }
        int ans=0,tans;
        while(bfs(sp,tp,372))
            while(tans=extendPath(sp,tp,372,sp,inf))
                ans+=tans;
        printf("%s\n",ans==tmp?"Yes":"No");
        ///printf("%d\n",-mcmf(sp,num,num));
    }
    return 0;
}

B 残余网络找割边,两次dfs找到S,T可到达点.枚举边即可(边cap=0,两点分别在S,T可达)

/**
邻接表转矩阵,正图,反置图

**/
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
const int MAXN=510;
int N,M,T;
int sp,tp;
int G[MAXN][MAXN];
int level[MAXN];
int vis[MAXN];
struct P{
    int u,v,cap,cost,nxt;
}node[MAXN*MAXN];int tot,head[MAXN];
void _DEBUG(int N,int (*G)[MAXN]){
    printf("\n");
    for(int i=0;i<=N;i++){
        printf("\n");
        for(int j=0;j<=N;j++)
            printf("%5d ",G[i][j]);
    }printf("\n");
}
bool bfs(int sp,int tp,int NN){
    memset(level,-1,sizeof(level));
    queue<int> Q;
    Q.push(sp);
    level[sp]=0;
    while(Q.size()){
        int u=Q.front();Q.pop();
        for(int i=head[u];~i;i=node[i].nxt) if(node[i].cap>0 && level[node[i].v]==-1){
            Q.push(node[i].v);
            level[node[i].v]=level[u]+1;
        }
    }
    return level[tp]!=-1;
}
int extendPath(int sp,int tp,int NN,int u,int beforemin){
    int res=0,t;
    if(u==tp || beforemin==0) return beforemin;
    for(int i=head[u];~i;i=node[i].nxt)
        if(node[i].cap>0 && level[node[i].v]==level[u]+1 && (t=extendPath(sp,tp,NN,node[i].v,min(beforemin,node[i].cap)))){
            node[i].cap-=t;
            node[i^1].cap+=t;
            beforemin-=t;
            res+=t;
            if(!beforemin) break;
    }
    if(!res) level[u]=-1;
    return res;
}

int pre[MAXN];
int dist[MAXN];
int spfa(int sp,int tp,int NN){
    queue<int> Q;
    memset(vis,0,sizeof(vis));
    memset(dist,0x3f,sizeof(dist));
    dist[sp]=0;vis[sp]=1;pre[sp]=-1;
    Q.push(sp);
    while(Q.size()){
        int u=Q.front();Q.pop();vis[u]=0;
        for(int i=head[u];~i;i=node[i].nxt) {
            int v=node[i].v;
            if(node[i].cap>0 && dist[v]>dist[u]+node[i].cost){
                dist[v]=dist[u]+node[i].cost;
                pre[v]=i;
                if(!vis[v])
                    vis[v]=1,Q.push(v);
                }
        }
    }
    return dist[tp]!=inf;
}

int mcmf(int sp,int tp,int NN){
    int ans=0,flow_sum=0;
    while(spfa(sp,tp,NN)){
        int t=inf;
        for(int i=pre[tp];i!=-1;i=pre[node[i].u])
            t=min(t,node[i].cap);
        for(int i=pre[tp];i!=-1;i=pre[node[i].u]){
            node[i].cap-=t;
            node[i^1].cap+=t;
        }ans+= dist[tp]*t;flow_sum+=t;
    }
    return ans;
}
void add(int a,int b,int c,int d){
    node[tot].u=a;node[tot].v=b;
    node[tot].cap=c;node[tot].cost=d;
    node[tot].nxt=head[a];head[a]=tot++;

    node[tot].u=b;node[tot].v=a;
    node[tot].cap=0;node[tot].cost=-d;
    node[tot].nxt=head[b];head[b]=tot++;
}
void dfs(int u,int vis[MAXN],int N){
    vis[u]=1;
    for(int i=1;i<N;i++) if(!vis[i] && G[u][i])
        dfs(i,vis,N);

}
int vis1[MAXN],vis2[MAXN];
int main(){
    while(~scanf("%d%d",&N,&M)){

        tot=0;sp=0;tp=N-1;
        memset(head,-1,sizeof(head));
        for(int i=0;i<M;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,c,0);
        }
        int tans;
        while(bfs(sp,tp,N))
            while(tans=extendPath(sp,tp,N,sp,inf));
        memset(G,0,sizeof(G));
        memset(vis1,0,sizeof(vis));
        for(int i=0;i<tot;i+=2) if(node[i].cap)
            G[node[i].u][node[i].v]=1;
        dfs(sp,vis1,N);
        memset(G,0,sizeof(G));
        memset(vis2,0,sizeof(vis));
        for(int i=0;i<tot;i+=2) if(node[i].cap)
            G[node[i].v][node[i].u]=1;
        dfs(tp,vis2,N);
        int ans=0;
        for(int i=0;i<tot;i+=2) if(node[i].cap==0 && vis1[node[i].u] && vis2[node[i].v])
            ans++;
        printf("%d\n",ans);
        ///printf("%d\n",-mcmf(sp,num,num));
    }
    return 0;
}

C 边是否可连受到限制.范围很小只有20,二分的话注意h的处理且不能l<=h

int main(){
    while(~scanf("%d%d",&N,&M)){
        int tans,ans;
        tot=0;sp=0;tp=N+M+1;
        memset(head,-1,sizeof(head));
        for(int i=1;i<=N;i++)
            for(int j=1;j<=M;j++) scanf("%d",in[i]+j);
        for(int i=1;i<=M;i++) scanf("%d",in[N+1]+i);
        int l=0,h=M;
        while(l<h){
            int len=(l+h)>>1;
            int flag=1;
            for(int s=1;s+len<=M && flag;s++){
                tot=0;memset(head,-1,sizeof(head));
                for(int k=1;k<=N;k++) add(sp,k,1,0);
                for(int k=1;k<=M;k++) add(k+N,tp,in[N+1][k],0);
                for(int k=1;k<=N;k++)
                    for(int t=s;t<=s+len;t++)
                        add(k,N+in[k][t],1,0);
                ans=0;
                while(bfs(sp,tp,tp+1))
                    while(tans=extendPath(sp,tp,tp+1,sp,inf))
                        ans+=tans;
                if(ans==N) flag=0;
            }
            if(flag) l=len+1;
            else h=len;
        }
        printf("%d\n",l+1);

D 欧拉混合图

/*
1 定义
欧拉通路 (Euler tour)——通过图中每条边一次且仅一次,并且过每一顶点的通路。
欧拉回路 (Euler circuit)——通过图中每条边一次且仅一次,并且过每一顶点的回路。
欧拉图——存在欧拉回路的图。
2 无向图是否具有欧拉通路或回路的判定
G有欧拉通路的充分必要条件为:G 连通,G中只有两个奇度顶点(它们分别是欧拉通路的两个端点)。
G有欧拉回路(G为欧拉图):G连通,G中均为偶度顶点。
3 有向图是否具有欧拉通路或回路的判定
D有欧拉通路:D连通,除两个顶点外,其余顶点的入度均等于出度,这两个特殊的顶点中,一个顶点的入度比出度大1,另一个顶点的入度比出度小1。
D有欧拉回路(D为欧拉图):D连通,D中所有顶点的入度等于出度。
4 混合图。混合图也就是无向图与有向图的混合,即图中的边既有有向边也有无向边。
5 混合图欧拉回路
混合图欧拉回路用的是网络流。
把该图的无向边随便定向,计算每个点的入度和出度。如果有某个点出入度之差为奇数,那么肯定不存在欧拉回路。因为欧拉回路要求每点入度 = 出度,也就是总度数为偶数,存在奇数度点必不能有欧拉回路。
现在每个点入度和出度之差均为偶数。将这个偶数除以2,得x。即是说,对于每一个点,只要将x条边反向(入>出就是变入,出>入就是变出),就能保证出 = 入。如果每个点都是出 = 入,那么很明显,该图就存在欧拉回路。
现 在的问题就变成了:该改变哪些边,可以让每个点出 = 入?构造网络流模型。有向边不能改变方向,直接删掉。开始已定向的无向边,定的是什么向,就把网络构建成什么样,边长容量上限1。另新建s和t。对于入 > 出的点u,连接边(u, t)、容量为x,对于出 > 入的点v,连接边(s, v),容量为x(注意对不同的点x不同。当初由于不小心,在这里错了好几次)。之后,察看是否有满流的分配。有就是能有欧拉回路,没有就是没有。查看流值 分配,将所有流量非 0(上限是1,流值不是0就是1)的边反向,就能得到每点入度 = 出度的欧拉图。
由于是满流,所以每个入 > 出的点,都有x条边进来,将这些进来的边反向,OK,入 = 出了。对于出 > 入的点亦然。那么,没和s、t连接的点怎么办?和s连接的条件是出 > 入,和t连接的条件是入 > 出,那么这个既没和s也没和t连接的点,自然早在开始就已经满足入 = 出了。那么在网络流过程中,这些点属于“中间点”。我们知道中间点流量不允许有累积的,这样,进去多少就出来多少,反向之后,自然仍保持平衡。
所以,就这样,混合图欧拉回路问题,解了。
*以上内容转自http://www.cnblogs.com/destinydesigner/archive/2009/09/28/1575674.html/

/*
混合图的欧拉回路,所谓混合图就是一个图中既有有向边,又有无向边
*/
#include <iostream>
#include <cmath>
#include <queue>
#define MAX_N 200
using namespace std;
int deg[MAX_N + 1];
int gf[MAX_N + 2][MAX_N + 2];
int f[MAX_N + 2][MAX_N + 2];
int total, m, s;
queue<int> bfsq;
bool v[MAX_N + 2];
int pre[MAX_N + 2];
int bfs()
{
    int i;
    for(i = 0; i <= m + 1; i++)
    {
        v[i] = false;
        pre[i] = -1;
    }
    v[0] = true;
    while(!bfsq.empty()) bfsq.pop();
    bfsq.push(0);
    while(!bfsq.empty())
    {
        int curNode;
        curNode = bfsq.front();
        bfsq.pop();
        for(i = 0; i <= m + 1; i++)
        {
            if(i == curNode || v[i] || !gf[curNode][i]) continue;
            v[i] = true;
            pre[i] = curNode;
            bfsq.push(i);
        }
    }
    if(pre[m + 1] == -1) return -1;
    int minVal = INT_MAX, curNode = m + 1, preNode;
    while((preNode = pre[curNode]) != -1)
    {
        if(gf[preNode][curNode] < minVal) minVal = gf[preNode][curNode];
        curNode = preNode;
    }
    return minVal;
}
bool admondsKarp(int total)
{
    int cutVal;
    while((cutVal = bfs()) != -1)
    {
        int curNode = m + 1, preNode;
        while((preNode = pre[curNode]) != -1)
        {
            gf[preNode][curNode] -= cutVal;
            gf[curNode][preNode] += cutVal;
            f[preNode][curNode] += cutVal;
            f[curNode][preNode] = - f[preNode][curNode];
            curNode = preNode;
        }
    }
    int totalVal = 0;
    for(int i = 1; i <= m; i++)
        totalVal += f[0][i];
    return totalVal == total;
}
int main()
{
    int caseN, i, from, to, type;
    scanf("%d", &caseN);
    while(caseN--)
    {
        scanf("%d%d", &m, &s);
        memset(deg, 0, sizeof(deg));
        memset(gf, 0, sizeof(gf));
        memset(f, 0, sizeof(f));
        for(i = 0; i < s; i++)
        {
            scanf("%d%d%d", &from, &to, &type);
            deg[from]++;
            deg[to]--;
            if(type != 1) gf[from][to]++;
        }
        bool can = true;
        for(i = 1; i <= m; i++)
        {
            if(abs(deg[i]) % 2 == 1)
            {
                can = false;
                break;
            }
            else deg[i] = deg[i] / 2;
        }
        if(can)
        {
            total = 0;
            for(i = 1; i <= m; i++)
            {
                if(deg[i] < 0) gf[i][m + 1] = abs(deg[i]);
                else if(deg[i] > 0)
                {
                    gf[0][i] = deg[i];
                    total += deg[i];
                }
            }
            if(!admondsKarp(total)) can = false;
        }
        if(!can) printf("impossible/n");
        else printf("possible/n");
    }
    return 0;
}

最小费用流问题

用spfa(有负边)找最短路径,增广即可..如果求最大值,将数值取相反值.

E

求使得区间覆盖不超过K次前提下,最大化权值.

每个区间可以选也可以不选, 区间两个数代表的点相连 cap=1,费用为负值,将相邻的边连起来,cap=K,费用0.( 表明可以正常往后流 )

/**
题解:

**/
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
const int MAXN=5010;
int N,M,T;

int level[MAXN];
int vis[MAXN];
struct P{
    int u,v,cap,cost,nxt;
}node[MAXN];int tot,head[MAXN];
void _DEBUG(int N,int (*G)[MAXN]){
    printf("\n");
    for(int i=0;i<=N;i++){
        printf("\n");
        for(int j=0;j<=N;j++)
            printf("%5d ",G[i][j]);
    }printf("\n");
}
bool bfs(int sp,int tp,int NN){
    memset(level,-1,sizeof(level));
    queue<int> Q;
    Q.push(sp);
    level[sp]=0;
    while(Q.size()){
        int u=Q.front();Q.pop();
        for(int i=head[u];~i;i=node[i].nxt) if(node[i].cap>0 && level[node[i].v]==-1){
            Q.push(node[i].v);
            level[node[i].v]=level[u]+1;
        }
    }
    return level[tp]!=-1;
}
int extendPath(int sp,int tp,int NN,int u,int beforemin){
    int res=0,t;
    if(u==tp || beforemin==0) return beforemin;
    for(int i=head[u];~i;i=node[i].nxt)
        if(node[i].cap>0 && level[node[i].v]==level[u]+1 && (t=extendPath(sp,tp,NN,node[i].v,min(beforemin,node[i].cap)))){
            node[i].cap-=t;
            node[i^1].cap+=t;
            beforemin-=t;
            res+=t;
            if(!beforemin) break;
    }
    if(!res) level[u]=-1;
    return res;
}

int pre[MAXN];
int dist[MAXN];
int spfa(int sp,int tp,int NN){
    queue<int> Q;
    memset(vis,0,sizeof(vis));
    memset(dist,0x3f,sizeof(dist));
    dist[sp]=0;vis[sp]=1;pre[sp]=-1;
    Q.push(sp);
    while(Q.size()){
        int u=Q.front();Q.pop();vis[u]=0;
        for(int i=head[u];~i;i=node[i].nxt) {
            int v=node[i].v;
            if(node[i].cap>0 && dist[v]>dist[u]+node[i].cost){
                dist[v]=dist[u]+node[i].cost;
                pre[v]=i;
                if(!vis[v])
                    vis[v]=1,Q.push(v);
                }
        }
    }
    return dist[tp]!=inf;
}

int mcmf(int sp,int tp,int NN){
    int ans=0,flow_sum=0;
    while(spfa(sp,tp,NN)){
        int t=inf;
        for(int i=pre[tp];i!=-1;i=pre[node[i].u])
            t=min(t,node[i].cap);
        for(int i=pre[tp];i!=-1;i=pre[node[i].u]){
            node[i].cap-=t;
            node[i^1].cap+=t;
        }ans+= dist[tp]*t;flow_sum+=t;
    }
    return ans;
}
void add(int a,int b,int c,int d){
    node[tot].u=a;node[tot].v=b;
    node[tot].cap=c;node[tot].cost=d;
    node[tot].nxt=head[a];head[a]=tot++;

    node[tot].u=b;node[tot].v=a;
    node[tot].cap=0;node[tot].cost=-d;
    node[tot].nxt=head[b];head[b]=tot++;
}
int data[MAXN][3];
int ar[MAXN<<2];
int lisan[MAXN*MAXN];
int main(){
    for(scanf("%d",&T);T;T--){
        scanf("%d%d",&N,&M);
        tot=0;
        memset(head,-1,sizeof(head));
        int sp=0,tp=N+M+1;
        int num=1;
        for(int i=1;i<=N;i++)
            scanf("%d%d%d",data[i]+0,data[i]+1,data[i]+2),ar[num++]=data[i][0],ar[num++]=data[i][1];
        sort(ar+1,ar+num);int j=1;
        for(int i=2;i<num;i++)
            if(ar[j]!=ar[i]) ar[++j]=ar[i];
        num=j+1;
        add(0,1,M,0);
        for(int i=1;i<num;i++)
            lisan[ar[i]]=i,add(i,i+1,M,0);
        for(int i=1;i<=N;i++) add(lisan[data[i][0]],lisan[data[i][1]],1,-data[i][2]);

        printf("%d\n",-mcmf(sp,num,num));
    }
    return 0;
}

H

求两条不重合的最短路径,cap=1,dist=费用.表达了边只能用一次.

/**
无向图的网络流只能用邻接表,因为其实是两个方向,每个方向都有自己的增广路,残余等
spfa,直接搞定
**/
#include<queue>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define inf 0x3f3f3f3f
const int MAXN=10010;
int N,M;
int cap[MAXN][MAXN],flow[MAXN][MAXN];///cap[][]网络中的容量,flow[][]流量分配
int cost[MAXN][MAXN];
int level[MAXN];
int vis[MAXN];
struct P{
    int u,v,cap,cost,nxt;
}node[MAXN<<3];int tot,head[MAXN];
void _DEBUG(int N,int (*G)[MAXN]){
    printf("\n");
    for(int i=0;i<=N;i++){
        printf("\n");
        for(int j=0;j<=N;j++)
            printf("%5d ",G[i][j]);
    }printf("\n");
}
bool bfs(int sp,int tp,int NN){
    memset(level,-1,sizeof(level));
    queue<int> Q;
    Q.push(sp);
    level[sp]=0;
    while(Q.size()){
        int u=Q.front();Q.pop();
        for(int v=1;v<=NN;v++) if(cap[u][v] && level[v]==-1){
            Q.push(v);
            level[v]=level[u]+1;
        }
    }
    return level[tp]!=-1;
}
int extendPath(int sp,int tp,int NN,int u,int beforemin){
    int res=0,t;
    if(u==tp || beforemin==0) return beforemin;
    for(int v=1;v<=NN;v++)
        if(cap[u][v] && level[v]==level[u]+1 && (t=extendPath(sp,tp,NN,v,min(beforemin,cap[u][v])))){
            cap[u][v]-=t;
            cap[v][u]+=t;
            beforemin-=t;
            res+=t;
            if(!beforemin) break;
    }
    if(!res) level[u]=-1;
    return res;
}

int pre[MAXN];
int dist[MAXN];
int spfa(int sp,int tp,int NN){
    queue<int> Q;
    memset(vis,0,sizeof(vis));
    memset(dist,0x3f,sizeof(dist));
    dist[sp]=0;vis[sp]=1;pre[sp]=-1;
    Q.push(sp);
    while(Q.size()){
        int u=Q.front();Q.pop();vis[u]=0;
        for(int i=head[u];~i;i=node[i].nxt) {
            int v=node[i].v;
            if(node[i].cap>0 && dist[v]>dist[u]+node[i].cost){
                dist[v]=dist[u]+node[i].cost;
                pre[v]=i;
                if(!vis[v])
                    vis[v]=1,Q.push(v);
                }
        }
    }
    return dist[tp]!=inf;
}

int mcmf(int sp,int tp,int NN){
    int ans=0,flow_sum=0;
    while(spfa(sp,tp,NN)){
        int t=inf;
        for(int i=pre[tp];i!=-1;i=pre[node[i].u])
            t=min(t,node[i].cap);
        for(int i=pre[tp];i!=-1;i=pre[node[i].u]){
            node[i].cap-=t;
            node[i^1].cap+=t;
        }ans+= dist[tp]*t;flow_sum+=t;
    }
    return ans;
}
void add(int a,int b,int c,int d){
    node[tot].u=a;node[tot].v=b;
    node[tot].cap=c;node[tot].cost=d;
    node[tot].nxt=head[a];head[a]=tot++;

    node[tot].u=b;node[tot].v=a;
    node[tot].cap=0;node[tot].cost=-d;
    node[tot].nxt=head[b];head[b]=tot++;
}
int main(){
    while(~scanf("%d%d",&N,&M)){
        tot=0;
        memset(head,-1,sizeof(head));
        add(0,1,2,0);
        add(N,N+1,2,0);
        for(int i=1;i<=M;i++){
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            add(a,b,1,c);
            add(b,a,1,c);
        }
        printf("%d\n",mcmf(0,N+1,N+1));
    }
    return 0;
}
时间: 2024-10-06 10:27:03

ACM网络流学习的相关文章

ACM课程学习总结

ACM课程学习总结报告 通过一个学期的ACM课程的学习,我学习了到了许多算法方面的知识,感受到了算法知识的精彩与博大,以及算法在解决问题时的巨大作用.此篇ACM课程学习总结报告将从以下方面展开: 学习ACM算法知识之前的具备的知识基础 学习过程及知识梳理 心得体会及收获 一,学习ACM算法知识之前具备的知识基础 在开始这一学期的课程之前,大一上学期及寒假期间我学习了C++标准库中的STL,了解了一些通用操作,各种类型的容器的特性,以及一些算法.关于算法,只学习了一些简单的遍历,递归.并未深入学习

学渣乱搞系列之网络流学习

学渣乱搞系列之网络流学习 几篇优秀的文章,鉴于本学渣目前功力不够,还不能掌握网络流的精髓要义.故载几篇牛文. Dinic算法: Comzyh的博客 Lich_Amnesia

《网络流学习笔记01--HDU3549》

1.网络流初步. 网络流是一个适用范围相当广泛的模型,相关的算法也很多,这里就几天学习网络流的相关知识做一个总结归纳. (1)最大流问题 如图所示,假设你需要把一些物品从结点s(称为源点)运送到结点t(称为汇点),可以从其他结点中转,图(a)中各条有向边的权表示最多能有多少个物品从这条边的起点直接运送到终点,例如图(a)从结点V3到V2最多可以运送9个物品. 图(b)给出了一种可能的最优方案,其中每条边中的第一个数字表示实际运送的物品数量,第二个数字表示题目中的上限, 我们把求解这样的问题称为最

hihoCoder 1394 : 网络流四&#183;最小路径覆盖 (网络流学习#4 记录)

题目链接:http://hihocoder.com/problemset/problem/1394 代码: #include<bits/stdc++.h> using namespace std; const int N=505*2+10,M=20005,INF=0x3f3f3f3f; int n,m; int c[N][N],pre[N]; int s,t; int bfs() { memset(pre,0,sizeof(pre)); queue<int>q; q.push(s)

《网络流学习笔记05--最小割最大流问题》

[图的割:定义] 指的是对于某个顶点集合S属于V,从S出发指向S外部的那些边的集合,记为割(S,V\S]),边的容量之和称为割的容量.如果有s属于S,而t属于V\S,那么此时的割称为s-t割,如果将网络中的s-t割所包含的边都割去,就无法找到从源点s到汇点t 的路径. 割:设Ci为网络N中一些弧的集合,若从N中删去Ci中的所有弧,即:使得从顶点Vs到顶点Vt的路集为空集时,称Ci为Vs和Vt间的一个割. 最小割:图中所有的割中,边权值和最小的割为最小割. 实例:如右图所示的割集为 --表示无序对

hihoCoder 1393 网络流三&#183;二分图多重匹配 (网络流学习#3 记录)

题目链接:http://hihocoder.com/problemset/problem/1393 话说我之前一直不知道二分匹配可以用网络流做... #include<cstdio> #include<cstring> #include<queue> using namespace std; const int N=205; struct ss{ int v,c,nxt; } e[N*20]; int head[N],tot,vis[N],n,m,a[N],b[N],s

hihoCoder 1369 网络流一&#183;Ford-Fulkerson算法 (网络流学习#1 记录)

题目链接:http://hihocoder.com/problemset/problem/1369 代码: #include<cstdio> #include<queue> #include<cstring> #include<algorithm> #include<cstdlib> #define INF 0x3f3f3f3f using namespace std; int mp[505][506]={0}; bool visit[506];

[补档][从入门到放弃]——网络流 学习索引

前篇 由于某篇博文已经长到我自己看不下去的地步,又不忍心删,所以就有了这篇索引,方便自己找,也方便来访的客人看嘛(真的会有人来看吗啊喂) 最长的博文 2017-7-29 大佬讲课笔记 网络流--从入门到放弃 没错,就是它= = 各种题解的索引 搭配飞行员 士兵占领 王者之剑 happiness 切糕 餐巾 血帆海盗

《网络流学习笔记02--Edmonds-Karp,Ford-Fulkerson,Dinic三种算法实现最大流》

[HDU3549]题目链接:click here 三种方法都用了一下,对比得出EK最少,只用46ms. [Edmonds-Karp算法] 基础的最大流算法,每次BFS寻找最短路进行增广,找出一条残余路径就可以了.然后对残余网络进行增广,不要忘记正向增广,相当于负向减少,也要在图中保存记录. 最后求一个割集来得到最大流,效率O(VE2),"找任意路径"最简单的方法是用DFS,但是数据要稍微增加就会变得较慢,采用BFS,源点和汇点保存在s和t中,净流量保存在变量f中. 代码: /*Edmo