网络流24题 (6/21)

来源:互联网 发布:emui8.0 知乎 编辑:程序博客网 时间:2024/06/05 05:14

flag待补全
6/21
提交地址:cogs

  • 一般dinic算法求最大流,E-k+bellman求费用流

1. [网络流24题] 搭配飞行员

思路: 二分图最大匹配建图
代码:

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#include <queue>using namespace std;const int maxn = 105,inf = 0x3f3f3f3f;struct node{    int to,next,cap,rev;    node(){}    node(int a,int b,int c,int d){to = a; next = b; cap = c; rev = d;}}edge[maxn*maxn<<1];int h[maxn],lev[maxn];int n,m,a,b;int num,s,t;void init(){    for(int i = 0; i < maxn; i ++) h[i] = -1;    num = 0; s = 0; t = n+1;}void add(int u,int v){    edge[num] = node(v,h[u],1,num+1); h[u] = num++;    edge[num] = node(u,h[v],0,num-1); h[v] = num++;}bool bfs(){    for(int i = s; i <= t; i++) lev[i] = -1;    lev[s] = 0;    queue<int> q;    q.push(s);    while(!q.empty())    {        int u = q.front(); q.pop();        for(int i = h[u] ;~i; i=edge[i].next)        {            int v = edge[i].to,cap = edge[i].cap;            if(lev[v] == -1 && cap)            {                lev[v] = lev[u] + 1;                q.push(v);            }        }    }    return lev[t] != -1;}int dfs(int u,int f){    if(u == t) return f;    for(int i = h[u]; ~i ; i = edge[i].next)    {        int v = edge[i].to,cap = edge[i].cap, rev = edge[i].rev;        if(lev[v] == lev[u] + 1 && cap)        {            int k = dfs(v,min(cap,f));            if(!k) continue;            edge[i].cap -= k;            edge[rev].cap +=k;            return k;        }    }    lev[u] = -2;    return 0;}void dinic(){    int flow = 0;    while(bfs())    {        while(int k = dfs(s,inf)) flow += k;    }    printf("%d\n",flow);}int main(){    freopen("flyer.in","r",stdin);    freopen("flyer.out","w",stdout);    scanf("%d%d",&n,&m);    init();    for(int i = 1; i <= m; i++) add(s,i);    while(~scanf("%d%d",&a,&b))    {        add(a,b);    }    for(int i = m+1; i <= n; i++) add(i,t);    dinic();    return 0;}

2. [网络流24题] 太空飞行计划

思路:
最大权闭合图,要求输出方案,最后选定的结点就是还存在于残量网络中的,也就是lev[i]还>0 的点
最大权闭合回路连图方案,对所有正权点连图S->v cap为v的值,而对所有负权点连边v->T,cap为负权取反,然后对所有的u>v连边,cap为inf,在这个图上跑最大流,最后的答案就为Sum(val[i]) (val[i]>0)-Max_flow;
代码:

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#include <queue>#include <sstream>using namespace std;const int maxn = 300,inf = 0x3f3f3f3f;struct node{    int to,next,cap,ori,rev;    node(){}    node(int a,int b,int c,int d,int e){to = a; next = b; cap = c; rev = d;ori = e;}}edge[maxn*maxn];int h[maxn],lev[maxn];int vis[maxn],used[maxn];int num,n,m,s,t;void init(){    memset(h,-1,sizeof(h));    for(int i = 0; i <= max(n,m); i++) vis[i] = used[i] = 0;    num = 0;    s = 0, t = n+m+1;}void add(int u,int v,int flow){    edge[num] = node(v,h[u],flow,num+1,flow); h[u] = num++;    edge[num] = node(u,h[v], 0 , num-1, 0); h[v] = num++;}bool bfs(){    for(int i = s; i <= t; i++) lev[i] = -1;    queue<int> q;    q.push(s); lev[s] = 0;    while(!q.empty())    {        int u = q.front(); q.pop();        for(int i = h[u] ;~i; i = edge[i].next)        {            int v = edge[i].to,cap = edge[i].cap;            //cout << v << endl;            if(lev[v] == -1 && cap)            {                lev[v] = lev[u] + 1;                q.push(v);            }        }    }    return lev[t] != -1;}int dfs(int u,int f){    if(u == t) return f;    for(int  i = h[u] ;~i ; i = edge[i].next)    {        int v = edge[i].to,cap = edge[i].cap, rev = edge[i].rev;        if(lev[v] == lev[u] + 1 && cap)        {            int k = dfs(v,min(f,cap));            if(!k) continue;            edge[i].cap -= k;            edge[rev].cap += k;            return k;        }    }    lev[u] = -2;    return 0;}int Max_flow(){    int flow = 0;    while(bfs())        while(int k = dfs(s,inf)) flow += k;    return flow;}char str[1005];int main(){    freopen("shuttle.in","r",stdin);    freopen("shuttle.out","w",stdout);    int a,b,c;    while(~scanf("%d%d",&n,&m))    {        init();        int sum = 0;        cin.getline(str,1005);        for(int i = 1; i <= n; i++)        {            cin.getline(str,1005);            stringstream ss;int temp;            ss << str;  ss >> temp;            sum += temp;            add(s,i,temp);            while(ss >> temp)                add(i, temp+n , inf);        }        for(int i = 1; i <= m; i++)            scanf("%d",&c),add(i+n,t,c);        int cnt = Max_flow();        for(int i = 1; i <= n; i++) if(lev[i] > 0) vis[i] = 1;        for(int i = 1+n; i <= n+m; i++) if(lev[i] > 0) used[i-n] = 1;        int flag = 0,mark = 0;        for(int i = 1; i <= n; i++)        {            if(vis[i])            {                if(flag) cout << " ";                cout << i;                flag = 1;            }        }        cout << endl;        for(int i = 1; i <= m; i++)        {            if(used[i] == 1)            {                if(mark) cout <<" ";                cout << i;                mark = 1;            }        }        cout << endl;        printf("%d\n", sum - cnt);    }    return 0;}  

3. [网络流24题] 最小路径覆盖问题

思路:
题目给出了思路….
对于求(DAG上的)最小路径覆盖问题,对所有点拆点,然后连边s->v(1->n), cap为1, v+n ->t(1->n),在图中有边的则连边 u->v+n,这里所有边的cap都为1。最后答案为n-Max_flow
这个题目要求输出路径,这里明显匹配了的边都是选定的边,所有我们只需要从头到尾找i->j,j->k,这种就够了。
代码:

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#include <queue>using namespace std;const int maxn = 300 + 5, maxe = maxn*maxn,inf = 0x3f3f3f3f;struct node{    int to,flow,rev,next;    node(){}    node(int a,int b,int c,int d){to = a;flow = b; rev = c; next = d;}    node(int a,int b){to = a;next = b;}}edge[maxe];int h[maxn],e[maxn],vis[maxn];int lev[maxn];int num,m,n,s,t,edgenum;void init(){    num = edgenum = 0;    for(int i = 0; i <= 2*n + 1; i++)        h[i] = -1,e[i] = -1,vis[i] = 0;    s = 0, t = 2*n+1;}void add(int u,int v,int cap){    edge[num] = node(v,cap,num+1,h[u]); h[u] = num++;    edge[num] = node(u,0,num-1,h[v]); h[v] = num++;}bool bfs(){    for(int i = s; i <= t; i++) lev[i] = -1;    queue<int> q;    q.push(s);  lev[s] = 0;    while(!q.empty())    {        int u = q.front(); q.pop();        for(int i = h[u];~i;i = edge[i].next)        {            int v = edge[i].to,cap = edge[i].flow;            if(lev[v] == -1 && cap)            {                lev[v] = lev[u] + 1;                q.push(v);            }        }    }    return lev[t] != -1;}int dfs(int u,int f){    if(u ==t )return f;    for(int i = h[u] ;~i; i =edge[i].next)    {        int v = edge[i].to, flow = edge[i].flow,rev = edge[i].rev;        if(lev[v] == lev[u] + 1 && flow)        {            int k = dfs(v, min (f,flow));            if(!k) continue;            edge[i].flow -= k;            edge[rev].flow += k;            return k;        }    }    lev[u] = -2;    return 0;}void Find(int u,int ori){    vis[u] = 1;    if(u == ori) cout << u;    else cout << " " << u;    if(e[u] != -1) Find(e[u],ori);}void Max_flow(){    int flow = 0,cnt = 0;    while(bfs())        while(int k = dfs(s,inf)) flow += k;    //cout << flow << endl;    for(int u = 1; u <= n; u++)    for(int i = h[u] ; ~i; i = edge[i].next)    {        int v = edge[i].to, flow = edge[i].flow;        if(v <= 2*n && v > n && flow == 0) e[u] = v-n;    }    for(int i = 1; i <= n; i++)   {        if(!vis[i]) {cnt++,Find(i,i);cout << endl;}    }    printf("%d\n",cnt);}int main(){    freopen("path3.in","r",stdin);    freopen("path3.out","w",stdout);    int a,b,c;    while(~scanf("%d%d",&n,&m))    {        init();        for(int i = 1; i <= n; i++) add(s,i,1);        for(int i = 0; i < m; i++)            scanf("%d%d",&a,&b),add(a,b+n,1);        for(int i = n+1 ; i <= n*2 ; i++)            add(i,t,1);        Max_flow();    }    return 0;}

4. [网络流24题]魔术球问题(简化版

思路: 可以看作i与j有边(满足i+j为一个完全平方数),所以这个题也可以看作上一题的最小路径覆盖问题。
代码:

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#include <vector>#include <queue>#include <cmath>using namespace std;const int maxn = 3205,maxe = 1e6,inf = 0x3f3f3f3f;struct node{    int to,cap,rev,next;    node(){}    node(int a,int b,int c,int d){to = a; cap = b; rev = c; next = d;}}edge[maxe<<1];int num,s,t,n;int h[maxn],lev[maxn];int ans[61];void add(int u,int v,int cap){    edge[num] = node(v,cap,num+1,h[u]); h[u] = num++;    edge[num] = node(u,0,num - 1,h[v]); h[v] = num++;}void init(){    for(int i = 0; i < maxn; i++) h[i] = -1;    s = num = 0;}bool bfs(){    for(int i = 0; i <= t; i++) lev[i] = -1;    queue<int> q;    q.push(s); lev[s] = 0;    while(!q.empty())    {        int u = q.front(); q.pop();        for(int i = h[u] ;~i; i = edge[i].next)        {            int v = edge[i].to,cap = edge[i].cap;            if(lev[v] == -1 && cap)            {                lev[v] = lev[u] + 1;                q.push(v);            }        }    }    return lev[t] != -1;}int dfs(int u,int f){    if(u == t) return f;    for(int i = h[u] ;~i; i = edge[i].next)    {        int v = edge[i].to,cap = edge[i].cap, rev = edge[i].rev;        if(lev[v] == lev[u] + 1 && cap)        {            int k = dfs(v,min(cap,f));            if(!k) continue;            edge[i].cap -= k;            edge[rev].cap += k;            return k;        }    }    lev[u] = -2;    return 0;}int Max_flow(){    int flow = 0;    while(bfs())    {        while(int k = dfs(s,inf)) flow += k;    }    return flow;}int main(){    freopen("balla.in","r",stdin);    freopen("balla.out","w",stdout);    t = 1600*2+1;    init();    int temp=0;    for(int i = 1; i <= 1600; i++)    {        add(s,i,1); add(i+1600,t,1);        for(int j = 1; j < i; j++)        {            int th = sqrt(i+j);            if(th*th == (i+j)) add(j,i+1600,1);        }        temp += Max_flow();        ans[i-temp] = i;    }    while(~scanf("%d",&n))        printf("%d\n",ans[n]);    return 0;}

5. [网络流24题] 圆桌聚餐

思路: 这个感觉就是直接连边……最后找一找残量网络中那些由不同单位到餐桌上flow为0的边,这代表这个单位到这个餐桌有一个代表。
代码:

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#include <queue>using namespace std;const int N = 150+5, M = 270+5, maxn = N+M, inf = 0x3f3f3f3f;struct node{    int to,next,cap,rev;    node(){}    node(int a,int b,int c,int d){to = a; next = b; cap = c; rev = d;}}edge[maxn*maxn];//int ans[maxn];int lev[maxn];int h[maxn];int num,s,t, n,m,sum;void init(){    for(int i = 0; i < (n+m)+2; i++)        h[i] = -1;    num = 0;    s = 0, t = n+m+1; sum = 0;}bool bfs(){    for(int i = s; i <= t; i++) lev[i] = -1;    lev[0] = 0;    queue<int> q;    q.push(0);    while(!q.empty())    {        int u = q.front(); q.pop();        for(int i = h[u]; ~i; i = edge[i].next)        {            int v = edge[i].to,cap = edge[i].cap;            if(lev[v] == -1 && cap)            {                lev[v] = lev[u] + 1;                q.push(v);            }        }    }    return lev[t] != -1;}int dfs(int u,int f){    if(u==t) return f;    for(int i = h[u]; ~i ; i = edge[i].next)    {        int v = edge[i].to,cap = edge[i].cap, rev = edge[i].rev;        if(lev[v] == lev[u] + 1 && cap)        {            int k = dfs(v,min(f,cap));            if(!k) continue;            edge[i].cap -= k;            edge[rev].cap += k;            return k;        }    }    lev[u] = -2;    return 0;}void add(int u,int v,int cap){    edge[num] = node(v,h[u],cap,num+1); h[u] = num++;    edge[num] = node(u,h[v],0,num-1); h[v] = num++;}void solve(){    int flow = 0;    while(bfs())        while(int k = dfs(0,inf)) flow += k;    if(flow != sum) printf("0\n");    else    {        printf("1\n");        for(int u = 1; u <= n; u++)        {            int flag = 0;            for(int i = h[u] ;~i; i = edge[i].next)            {                int v = edge[i].to, cap = edge[i].cap;                if(cap == 0)                {                    if(flag) printf(" ");                    printf("%d",v-n);                    flag = 1;                }            }            printf("\n");        }    }}int main(){    freopen("roundtable.in","r",stdin);    freopen("roundtable.out","w",stdout);    int a;    while(~scanf("%d%d",&n,&m))    {        init();        for(int i = 1; i <= n; i++) scanf("%d",&a), add(s,i,a), sum += a;        for(int i = 1; i <= m; i++) scanf("%d",&a), add(n+i,t,a);        for(int i = 1; i <= n; i++)        for(int j = 1; j <= m; j++)            add(i,n+j,1);        solve();    }    return 0;}

6. [网络流24题] 运输问题

思路: 这个连边就是直接连,然后求一遍最小费用流,我在求最大费用的时候是把d[i]置成-inf,然后求的最长路。
代码:

//求最大费用流与最小费用流#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#include <queue>using namespace std;#define ll long longconst int inf = 0x3f3f3f3f,maxn = 200 + 10,maxe = 2e4 + 10;struct node{    int to,cap,w,rev,next,from;    node(){}    node(int a,int b,int c,int d,int e,int f)    {to = a; cap = b; w = c; rev = d; next = e;from = f;}}edge[maxe<<1],edge1[maxe << 1];int h[maxn],h1[maxn],d[maxn],a[maxn],inq[maxn],pre[maxn];int num,num1,s,t,n,m;void init(){    s = num = num1 = 0; t = n+m+1;    for(int i = 0; i < maxn; i++) h[i] = h1[i] = -1;}void add(int u,int v,int cap,int w){    edge[num]   = node(v,cap,w,num+1,h[u],u);   h[u] = num++;    edge[num]   = node(u,0,-w,num-1, h[v],v);   h[v] = num++;    edge1[num1] = node(v,cap,w,num1+1,h1[u],u); h1[u] = num1++;    edge1[num1] = node(u,0,-w,num1-1, h1[v],v); h1[v] = num1++;}int bfs(){    for(int i = 0; i < maxn ; i++)        d[i] = inf, a[i] = 0, inq[i] = 0;    queue<int> q;    q.push(0);  d[0] = 0, a[0] = inf, inq[0] = 1;    while(!q.empty())    {        int u = q.front(); q.pop();        inq[u] = 0;        for(int i = h[u] ;~i; i = edge[i].next)        {            int v = edge[i].to, cap = edge[i].cap,w = edge[i].w;            if(cap && d[v] > d[u] + w)            {                d[v] = d[u] + w;                a[v] = min(a[u], cap);                pre[v] = i;                if(!inq[v]){q.push(v); inq[v] = 1;}            }        }    }    if(d[t] == inf) return 0;    for(int u = t; u != s; u = edge[pre[u]].from)    {        int rev = edge[pre[u]].rev;        edge[pre[u]].cap -= a[t];        edge[rev].cap += a[t];    }    return d[t]*a[t];}int bfs1(){    for(int i = 0; i < maxn ; i++)        d[i] = -inf, a[i] = 0, inq[i] = 0;    queue<int> q;    q.push(0);  d[0] = 0, a[0] = inf, inq[0] = 1;    while(!q.empty())    {        int u = q.front(); q.pop();        inq[u] = 0;        for(int i = h1[u] ;~i; i = edge1[i].next)        {            int v = edge1[i].to, cap = edge1[i].cap , w = edge1[i].w;            if(cap && d[v] < d[u] + w)            {                d[v] = d[u] + w;                a[v] = min(a[u], cap);                pre[v] = i;                if(!inq[v]){q.push(v); inq[v] = 1;}            }        }    }    if(d[t] == -inf) return 0;    for(int u = t; u != s; u = edge1[pre[u]].from)    {        int rev = edge1[pre[u]].rev;        edge1[pre[u]].cap -= a[t];        edge1[rev].cap += a[t];    }    return d[t]*a[t];}void E_K1(){    ll res = 0;    while(int k = bfs()) res += k;    printf("%lld\n",res);}void E_K2(){    ll res = 0;    while(int k = bfs1()) res += k;    printf("%lld\n",res);}int main(){    freopen("tran.in","r",stdin);    freopen("tran.out","w",stdout);    int a;    scanf("%d%d",&n,&m);    init();    for(int i = 1; i <= n; i++)scanf("%d",&a), add(s,i,a,0);    for(int i = 1; i <= m; i++)scanf("%d",&a), add(i+n,t,a,0);    for(int i = 1; i <= n; i++)    for(int j = 1; j <= m; j++)scanf("%d",&a),add(i,j+n,inf,a);    E_K1();    E_K2();    return 0;}
原创粉丝点击