ACM网络流学习

11 篇文章 0 订阅

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

之前写过 关于网络流的算法入门,其实那么多会一个就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;
}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值