网络流24题 (6/21)

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 long
const 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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值