不懂就问,卡常后的Dinic比没有卡常的还慢

P3376 【模板】网络最大流

char_cha_ch @ 2022-11-30 18:26:34

卡常:

#include<stdio.h>
#define N 209
#define M 5009

struct node
{
    int v, w, nxt;
}e[M << 1];

int head[N], cnt = 1;

inline void add(int u, int v, int w)
{
    e[++ cnt].v = v, e[cnt].w = w, e[cnt].nxt = head[u], head[u] = cnt;
}

inline int read()
{
    register int ret = 0;
    register bool f = 1;
    register char ch = getchar();
    while (ch < '0' || ch > '9')
        (ch == '-') ? f = 0 : 0, ch = getchar();
    while (ch >= '0' && ch <= '9')
        ret = (ret << 1) + (ret << 3) + (ch ^ 48), ch = getchar();
    return f ? ret : -ret;
}

int s, t;

int dis[N], now[N];

#include<cstring>

using std::memset;

#include<queue>

using std::queue;

inline bool bfs()
{
    memset(dis, -1, sizeof dis);
    register int u, v, w, i;
    queue < int > q;
    q.push(s);
    dis[s] = 0;
    now[s] = head[s];
    while (!q.empty())
    {
        u = q.front();
        q.pop();
        for (i = head[u];i;i = e[i].nxt)
        {
            v = e[i].v, w = e[i].w;
            if (w > 0 && dis[v] == -1)
            {       
                dis[v] = dis[u] + 1;
                now[v] = head[v];
                if (v == t)
                    return 1;
                q.push(v);
            }
        }
    }
    return 0;
}

inline int min(int a, int b)
{
    return a < b ? a : b;
}

#define ll long long

int dfs(register int u, register int flow = 1e9)
{
    if (u == t)
        return flow;
    register int lost = flow, v, w, k;
    for (register int i = now[u];i && lost;i = e[i].nxt, now[u] = i)
    {
        v = e[i].v, w = e[i].w;
        if (dis[v] == dis[u] + 1 && w > 0)
        {
            k = dfs(v, min(lost, w));
            if (!k)
            {
                dis[v] = -1;
                continue;
            }
            lost -= k;
            e[i].w -= k;
            e[i ^ 1].w += k;
        }
    }
    return flow - lost;
}

int main()
{
    register int n = read(), m = read(), u, v, w;
    register ll ans = 0;
    s = read(), t = read();
    while (m --)
        u = read(), v = read(), w = read(), add(u, v, w), add(v, u, 0);
    while (bfs())
        ans += dfs(s);
    printf("%lld", ans);

    return 0;
}

未卡常:

#include<stdio.h>
#define N 409
#include<queue>
using namespace std;
#define M 10009

int dis[N];

struct node
{
    int v, w, nxt;
}e[M << 1];

int head[N], cnt = 1, now[N];

inline void add(int u, int v, int w)
{
    e[++ cnt].v = v, e[cnt].w = w, e[cnt].nxt = head[u], head[u] = cnt;
}

inline int read()
{
    register int ret = 0, f = 1;
    register char ch = getchar();
    while (ch < '0' || ch > '9')
        (ch == '-') ? f = -1 : 0, ch = getchar();
    while (ch >= '0' && ch <= '9')
        ret = (ret << 1) + (ret << 3) + (ch ^ 48), ch = getchar();
    return ret * f;
}

int n, s, t;

inline bool bfs()
{
    for (register int i = 1;i <= n;++ i)
        dis[i] = -1;
    queue < int > q;
    dis[s] = 0;
    q.push(s);
    now[s] = head[s];
    while (!q.empty())
    {
        register int u = q.front();
        q.pop();
        for (register int i = head[u];i;i = e[i].nxt)
            if (e[i].w > 0 && dis[e[i].v] == -1)//构建分层图
            {
                int v = e[i].v;
                now[v] = head[v];
                dis[v] = dis[u] + 1;
                if (v == t)
                    return 1;//找到有路径
                q.push(v);
            }
    }
    return 0;//没找到
}

inline long long min(long long a, long long b) {return a < b ? a : b;}

long long dfs(register int u, register long long sum = 1e9)
{
    if (u == t)
        return sum;
    register long long lost = sum;
    for (register int i = now[u];i && lost;i = e[i].nxt)
    {
        now[u] = i;
        int v = e[i].v, w = e[i].w;
        if (dis[v] == dis[u] + 1 && w > 0)//保证不是同一个层并且可以流动
        {
            int k = dfs(v, min(w, lost));//尽量让少的去流,也就是 EK 的想法
            if (!k)//没有,那就真没有
            {
                dis[e[i].v] = -1;
                continue;
            }
            lost -= k;
            e[i].w -= k;
            e[i ^ 1].w += k;
        }
    }
    return sum - lost;
}

inline long long Dinic()
{
    register long long ret = 0;
    while (bfs())
        ret += dfs(s);
    return ret;
}

int main()
{
    n = read();
    register int m = read();
    s = read(), t = read();
    register int u, v, w;
    while (m --)
        u = read(), v = read(), w = read(), add(u, v, w), add(v, u, 0);
    printf("%lld", Dinic());

    return 0;
}

by Register_int @ 2022-11-30 18:42:17

你的当前弧呢(


by _slb @ 2022-11-30 19:19:06

加上当前弧优化


by reveal @ 2022-11-30 19:28:03

@Register_int @_slb

其实是有当前弧优化的:

for (register int i = now[u];i && lost;i = e[i].nxt, now[u] = i)

@kirihara233

这里引用 rvalue 在 [教程]网络流详解: 从入门到放弃 中的两句话:

然后最关键的决定时间复杂度的优化是当前弧优化, 我们每次向某条边的方向推流的时候, 肯定要么把推送量用完了, 要么是把这个方向的容量榨干了. 除了最后一条因为推送量用完而无法继续增广的边之外其他的边一定无法继续传递流量给 t

为啥我要把一个 Dinic 讲得这么细呢? 况且它确实真的只是个建模之后跑一下的工具?

因为如果你不理解 Dinic 的过程与复杂度分析, 你几乎一 定 会 写 假.

有些看起来很不起眼的小细节可能影响着整个算法的时间复杂度.

首先就是当前弧优化的跳出条件, 我为啥要把"除了最后一条边之外"那句话加粗呢? 因为你如果把跳出判定写在 for 循环里会慢 10 倍以上, 根本不是常数问题, 是复杂度出了锅. 因为你会漏掉最后那个可能没跑满的弧, 而分层图 BFS 会在当前图没有被割断的时候一直跑跑跑, 于是就锅了.

你需要在 lost 以及流量计算完毕之后立刻判断 lost 是否为 0 并跳出,而不是在 for 中判断。

另外这都什么年代了还在用 register 卡常。


by Register_int @ 2022-11-30 19:29:00

@reveal 对不起眼瞎了(


by char_cha_ch @ 2022-11-30 22:05:27

@reveal 谢谢大佬!(完了,现在用 register 用惯了,改不过来了


by 聊机 @ 2023-01-07 20:48:15

@reveal 它那个lost没有问题吧,实现方式完全不一样啊,原文里用的指针,那个当前弧优化的更新是在for循环里的,而楼主写的当前弧优化是在下面的,也就是说for里面的那个判断是并没有更新当前弧优化


by 聊机 @ 2023-01-07 20:48:45

@reveal 这改不改都一样的


by 聊机 @ 2023-01-07 21:22:54

@kirihara233 我觉得你变慢的原因就是所谓卡常加的register,register不能乱加吧,register只应该用于不需要保存的中间量,而且O2会自动帮你开一些(或者只是评测机波动),你这“不卡常”都加快读,你所谓的卡不卡有什么区别呢?


by char_cha_ch @ 2023-01-07 21:29:11

@聊机 额,现在这个帖子已经没什么意义了吧,问题都解决了。


|