Dinic算法 (洛谷P2763)

昨天刚学的dinic算法,在这里记录一下

dinic算法与普通方法求最大流的思路类似,都是不断地寻找增广路并不断更新。而dinic算法新加入了一个设置分层图的方法,通过分层避免了增广路径因为反向边的添加,dfs时的盲目性导致增广效率低下的问题。而分层只需要设一个depth数组,然后每次bfs一下就好了。当bfs之后终点的深度不存才时,即不存在分层图,则增广路寻找完毕。(优化一)

然后就是从一个点找到一条增广路之后,可以继续从这个点开始找下一条增广路。这就避免了找到一条路之后,要找下一条就必须从头开始的情况了。(优化二)

再者,一次bfs之后可以进行多次dfs寻找增广路,没必要一次bfs一次dfs。(优化三)

此外,dinic算法还可以有一个优化。就是通过邻接表存图的时候,可以再开一个cur数组存放当前点增广到哪条边。这样dfs时就不用都从i = head[u]开始找,节省了一定的时间。不过要注意每次分层之后,因为分层变了,所以要将cur都初始化为第一条边即head。

题:洛谷 P2763 试题库问题

#include <iostream>
#include <cstring>
#include <queue>

using namespace std;

#define mem(a, i) memset(a, i, sizeof a)

const int maxn = 30030;
const int inf = 0x3f3f3f3f;

class Dinic
{
private:
    int ans, cnt;
    int n, s, t;
    int type, maxk;
    int head[maxn], next[maxn];
    int W[maxn], V[maxn];
    int depth[maxn], cur[maxn];
    
    void init(int kk, int nn);
    void add(int u, int v, int w);
    bool bfs();
    int dfs(int u, int dist);
    void dinic();
    void show();

public:
    Dinic(int kk, int nn);
    ~Dinic();
};

Dinic::Dinic(int kk, int nn) : n(kk + nn + 2), t(kk + nn + 1), type(kk)
{
    s = 0;
    ans = 0;
    cnt = -1;
    maxk = 0;
    mem(head, 0xff);
    mem(next, 0xff);

    init(kk, nn);
    dinic();
}

Dinic::~Dinic()
{
    Dinic::show();
}


void Dinic::add(int u, int v, int w)
{
    cnt ++;
    W[cnt] = w;
    V[cnt] = v;
    next[cnt] = head[u];
    head[u] = cnt ++;
    W[cnt] = 0;
    V[cnt] = u;
    next[cnt] = head[v];
    head[v] = cnt;
}

void Dinic::init(int kk, int nn)
{
    int x;
    for(int i = 1; i <= kk; ++ i)
    {
        cin >> x;
        maxk += x;
        add(0, i, x);
    }
    int p;
    for(int i = 1; i <= nn; ++ i)
    {
        add(kk + i, t, 1);
        cin >> p;
        for(int j = 1; j <= p; ++ j)
        {
            cin >> x;
            add(x, kk + i, 1);
        }
    }
}

bool Dinic::bfs()
{
    mem(depth, 0);
    depth[s] = 1;
    queue <int> q;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        for(int i = head[u]; ~i; i = next[i])
        {
            if(W[i] && !depth[V[i]])
            {
                depth[V[i]] = depth[u] + 1;
                if(V[i] == t)
                {
                    return true;
                }
                q.push(V[i]);
            }
        }
    }
    return false;
}

int Dinic::dfs(int u, int dist)
{
    if(u == t)
    {
        return dist;
    }
    int tmp = 0;
    for(int& i = cur[u]; ~i; i = next[i])
    {
        if(W[i] && depth[V[i]] == depth[u] + 1)
        {
            int di = dfs(V[i], min(W[i], dist));
            if(di)
            {
                W[i] -= di;
                W[i ^ 1] += di;
                tmp += di;        //优化二
                dist -= di;
                if(dist <= 0)
                {
                    break;
                }
            }
        }
    }
    return tmp;
}

void Dinic::dinic()
{
    while(bfs())       //优化一
    {
        for(int i = 0; i <= t; ++ i)
        {
            cur[i] = head[i];
        }
        while(int di = dfs(s, inf))       //优化三
        {
            ans += di;
        }
    }
}

void Dinic::show()
{
    if(ans == maxk)
    {
        for(int i = 1; i <= type; ++ i)
        {
            cout << i << ':';
            for(int j = head[i]; ~j; j = next[j])
            {
                if(!W[j] && V[j] > type)
                {
                    cout << ' ' << V[j] - type;
                }
            }
            cout << endl;
        }
    }
    else
    {
        cout << "No Solution!\n";
    }
}

int main()
{
    int k, n;
    cin >> k >> n;
    Dinic(k, n);
    return 0;
}

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值