zoukankan      html  css  js  c++  java
  • The 2016 ACM-ICPC Asia Qingdao Regional Contest

    前置知识

    网络流

    一些常见的定义:

    容量:每条边都有一个容量(水管的最大水流容量)

    源点:出发点(水厂)。

    汇点:结束点(废水站)。

    流:一个合法解称作一个流,也就是一条可以从源点到汇点的一条合法路径。

    流量:每条边各自被经过的次数称作其流量,最终收集的总数为整个流的流量。

    Dinic (n^2*m)

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 5e3+5,inf=0x3f3f3f3f;
    int n, m, s, t, x, y, z;
    int level[N];//level是记录一个点有没有被遍历到|选取,如果最后t点不能被遍历到就结束
    struct node
    {
        int link;
        int w;
        int position;
    };
    vector<node> f[N];
    queue<int> q;
    int dfs(int x,int flow)
    {
        int ans = 0;
        if(x==t||flow==0)
            return flow;
        for(auto &i:f[x])//结构体无法在外部改变,外面只是创建了一个临时变量(相当结构体内部变量是私有属性)
        {
            if(i.w>0&&level[i.link]==level[x]+1)// 保证边权不为0 且按照深度dfs
            {
                int d = dfs(i.link, min(flow, i.w)); // 搜索,flow是最小的所以是min
                if(d==0)
                    level[i.link] = 0;
                if(d>0)
                {
                    i.w -= d;//正向边减少
                    f[i.link][i.position].w += d;// 反向边增加
                    flow -= d;
                    ans += d;
                    if(flow==0)
                        break;
                }
            }
        }
        return ans;
    }
    bool bfs()
    {
        q.push(s);
        memset(level, -1, sizeof(level));
        level[s] = 0;
        while(!q.empty())
        {
            int temp = q.front();
            q.pop();
            for(auto &i:f[temp])
            {
                if(i.w==0||level[i.link]!=-1)
                    continue;
                level[i.link] = level[temp] + 1;
                q.push(i.link);
            }
        }
        if(level[t]!=-1)
            return 1;
        return 0;
    }
    long long dinic()
    {
        long long ans = 0;
        while(bfs())
            ans += dfs(s, inf);
        
        return ans;
    }
    

    网络流模板

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 5e3+5,inf=0x3f3f3f3f;
    int n, m, s, t, x, y, z;
    int level[N];
    struct node
    {
        int link;
        int w;
        int position;
    };
    vector<node> f[N];
    queue<int> q;
    int dfs(int x,int flow)
    {
        int ans = 0;
        if(x==t||flow==0)
            return flow;
        for(auto &i:f[x])//结构体无法在外部改变,外面只是创建了一个临时变量(相当结构体内部变量是私有属性)
        {
            if(i.w>0&&level[i.link]==level[x]+1)// 保证边权不为0 且按照深度dfs
            {
                int d = dfs(i.link, min(flow, i.w)); // 搜索,flow是最小的所以是min
                if(d==0)
                    level[i.link] = 0;
                if(d>0)
                {
                    i.w -= d;//正向边减少
                    f[i.link][i.position].w += d;// 反向边增加
                    flow -= d;
                    ans += d;
                    if(flow==0)
                        break;
                }
            }
        }
        return ans;
    }
    bool bfs()
    {
        q.push(s);
        memset(level, -1, sizeof(level));
        level[s] = 0;
        while(!q.empty())
        {
            int temp = q.front();
            q.pop();
            for(auto &i:f[temp])
            {
                if(i.w==0||level[i.link]!=-1)
                    continue;
                level[i.link] = level[temp] + 1;
                q.push(i.link);
            }
        }
        if(level[t]!=-1)
            return 1;
        return 0;
    }
    long long dinic()
    {
        long long ans = 0;
        while(bfs())
            ans += dfs(s, inf);
        
        return ans;
    }
    int main()
    {
        scanf("%d %d %d %d", &n,&m,&s,&t);
        for (int i = 1; i <= m;i++)
        {
            scanf("%d %d %d", &x, &y, &z);
            f[x].push_back((node){y, z,(int)f[y].size()});
            f[y].push_back((node){x, 0,(int)f[x].size()-1});// 获取x在y的第几条边
        }
        printf("%lld
    ", dinic());
        return 0;    
    } 
    

    费用流

    主要都是考建图(树上dp),没什么好说的(其实是说不清楚)

    板子也就是ek上面改个spfa|dij 也有zkw费用流(dinic+spfa)

    主要把网络流的思想理解就行了

    The 2016 ACM-ICPC Asia Qingdao Regional Contest

    G Coding Contest

    参考这个博客

    题意:

    N 个不相交区域,共有 M 条有向路连接两个区域

    对于每个区域,给出 s[i],b[i]代表区域内有 s[i] 个人,b[i] 个餐包,一旦某人在本区域内拿不到餐包,就会前往其他区域获取餐包,

    而众所周知,赛场上的路上是有很多电线的,一不小心就会踢到电线,所以现在每条路上都存在这一些电线,

    现在已知,一旦某个选手走过第 i 条有向边,就有 p[i] 的概率踢到电线,进而影响整个电网,不过经过该路径的第一个人是必然不会踢到电线的,同时对于第 i 条边,限制最多 c[i] 个人走过。

    现在,求整个电网被影响的最小概率。

    (P=1-sum_{1}^{n}(1-p_i)^{flow[i]})

    所以要P最小,后面那个求和最大,但是我们板子的mcost是mcost+=flow[i]*dis[i]

    所以可以去对数变成这种形式(flow_i*log(1-p_i)) 也就是权值变成了(log(1-p_i)),然后跑最大花费(就是权值取反,其他不变)完事记得pow回来

    但是第一个人一定不会踢到电线,所以我们第一次经过某个 '汇点' 的权值是0 将一条边拆为 add_edge(u,v,1,0)和 add_edge(u,v,c[i]-1,-log(1-p[i]))

    直接用log精度居然爆了,学到了学到了

    log2刚好是0.5 其他的都会爆炸

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 5e3+5,inf=0x3f3f3f3f;
    int T,n, m, qx,zx,x,y,s[N],b[N],c[N];
    int pre[N],flow[N],pos[N];
    bool use[N];
    double p[N],mcost,dis[N];
    long long maxflow;
    // pre记录回溯前驱,dis是 cost*w ,pre取代了level数组
    struct node
    {
        int link;
        int w;
        double cost;
        int position;
    };
    vector<node> f[N];
    queue<int> q;
    void add_edge(int u,int v,int w,double cost)
    {
        //建立u->v的正向边
        f[u].push_back((node){v, w,cost,(int)f[v].size()});
        f[v].push_back((node){u, 0,-cost,(int)f[u].size()-1});
        // 注意花费是相反数
    }
    bool spfa()//最短路
    {
        while(!q.empty())
            q.pop();
        for (int i = 0; i <=n+1;i++)
        {
            dis[i] = inf;
            use[i] = 0;
            pre[i] = -1;
        }
        use[qx] = 1;
        flow[qx] = inf;
        dis[qx] = 0;
        pre[qx] = 0;
        q.push(qx);
        while(!q.empty())
        {
            int temp = q.front();
            q.pop();
            use[temp] = 0;
            for (int i = 0; i < f[temp].size();i++)
            {
                node xx = f[temp][i];
                if(xx.w>0&&dis[xx.link]>dis[temp]+xx.cost+1e-18)
                {
                    dis[xx.link] = dis[temp] + xx.cost;
                    pre[xx.link] = temp;
                    flow[xx.link] = min(flow[temp],xx.w);
                    pos[xx.link] = i;//记录这个点是temp的第几条边
                    if(use[xx.link]==0)
                    {
                        use[xx.link] = 1;
                        q.push(xx.link);
                    }
                }
            }
        }
        // for (int i = 0; i <= n+m+1;i++)
        //     printf("i=%d %d %d
    ", i,dis[i],pre[i]);
        if (pre[zx] == -1)
            return 0;
        return 1;
    }
    void update()
    {
        int now = zx;
        while(now!=qx)
        {
            node &temp = f[pre[now]][pos[now]];
            temp.w -= flow[zx];
            node &temp2=f[now][temp.position];
            temp2.w+= flow[zx];
            now = pre[now];
        }
        mcost += flow[zx] * dis[zx];
        maxflow += flow[zx];
    }
    void mfmc()
    {
        qx = 0;
        zx = n + 1;
        memset(flow, 0, sizeof(flow));
        mcost = 0;
        maxflow = 0;
        while(spfa())
        {
            update();
        }
    }
    int main()
    {
        scanf("%d", &T);
        while(T--)
        {
            scanf("%d %d", &n,&m);
            for (int i = 1; i <= n;i++)
                f[i].clear();
            for (int i = 1; i <= n;i++)
            {
                scanf("%d %d", &x,&y);
                if(x>y)//需要流出人,所以相当于源点
                {
                    add_edge(0, i, x - y, 0);
                }
                else//需要流入,相当于汇点
                {
                    add_edge(i, n + 1, y - x, 0);
                }
            }
            for (int i =1; i <= m;i++)
            {
                scanf("%d %d %d %lf",&x,&y,&c[i],&p[i]);
                add_edge(x,y,c[i]-1, -(log(1-p[i])/log(2)));
                add_edge(x, y, 1, 0);       //第一个人cost是0
            }
            mfmc();
            printf("%.2lf
    ", 1-pow(2,-mcost));
        }
        return 0;    
    } 
    /*
    2
    4 4
    2 0
    0 3
    3 0
    0 3
    1 2 5 0.5
    3 2 5 0.5
    1 4 5 0.5
    3 4 5 0.5
    4 4
    2 0
    0 3
    3 0
    0 3
    1 2 5 0.5
    3 2 5 0.5
    1 4 5 0.5
    3 4 5 0.5
    */
    

    最后附上dinic+spfa板子(n^2*m)再也不用ek了

    #include<bits/stdc++.h>
    using namespace std;
    const int N = 1006,const_w=2e7+5;
    const long long inf = 1e17;
    int T,n, m, qx,zx,a,b,c,d,e,r[N];
    int pre[N];
    bool use[N];
    long long mcost,dis[N],flow[N],maxflow;
    // pre记录回溯前驱,dis是 cost*w ,pre取代了level数组
    struct node
    {
        int link;
        int w;
        int cost;
        int position;
    };
    vector<node> f[N];
    queue<int> q;
    void add_edge(int u,int v,int w,int cost)
    {
        //建立u->v的正向边
        f[u].push_back((node){v, w,cost,(int)f[v].size()});
        f[v].push_back((node){u, 0,-cost,(int)f[u].size()-1});
        // 注意花费是相反数
    }
    bool spfa()//最短路
    {
        while(!q.empty())
            q.pop();
        for (int i = 0; i <=2*n+1;i++)
        {
            dis[i] = inf;
            use[i] = 0;
            pre[i] = -1;
        }
        use[qx] = 1;
        flow[qx] = inf;
        dis[qx] = 0;
        pre[qx] = 0;
        q.push(qx);
        while(!q.empty())
        {
            int temp = q.front();
            q.pop();
            use[temp] = 0;
            for (int i = 0; i < f[temp].size();i++)
            {
                node xx = f[temp][i];
                if(xx.w>0&&dis[xx.link]>dis[temp]+xx.cost)
                {
                    dis[xx.link] = dis[temp] + xx.cost;
                    pre[xx.link] = temp;
                    flow[xx.link] = min(flow[temp],(long long)xx.w);
                    if(use[xx.link]==0)
                    {
                        use[xx.link] = 1;
                        q.push(xx.link);
                    }
                }
            }
        }
        if (pre[zx] == -1)
            return 0;
        return 1;
    }
    long long dfs(int x,long long flow)
    {
        long long ans = 0;
        if(x==2*n+1||flow==0)
            return flow;
        for(auto &i:f[x])//结构体无法在外部改变,外面只是创建了一个临时变量(相当结构体内部变量是私有属性)
        {
            if(i.w>0&&dis[i.link]==dis[x]+i.cost)// 保证边权不为0 且按照深度dfs
            {
                long long d = dfs(i.link, min(flow,(long long) i.w)); // 搜索,flow是最小的所以是min
                if(d>0)
                {
                    i.w -= d;//正向边减少
                    f[i.link][i.position].w += d;// 反向边增加
                    flow -= d;
                    ans += d;
                    mcost += d * i.cost; //比dinic多了一个这个
                    if(flow==0)
                        break;
                }
            }
        }
        return ans;
    }
    void mfmc()
    {
        qx = 0;
        zx = 2*n + 1;
        memset(flow, 0, sizeof(flow));
        mcost = 0;
        maxflow = 0;
        while(spfa())
        {
            maxflow+=dfs(0,inf);
        }
    }
    int main()
    {
        scanf("%d %d", &n,&m);
        for (int i = 1; i <= m;i++)
        {
            scanf("%d %d %d", &a,&b,&c);
            add_edge(a, b+n, 1, -c);
        }
        for (int i = 1; i <= n;i++)
        {
            add_edge(0, i, 1, 0);
            add_edge(i + n, 2 * n + 1, 1, 0);
        }
        mfmc();
        printf("%lld
    ",-mcost);
        for (int i = 1; i <= n;i++)
        {
            for(auto j:f[i+n])
            {
                if(j.w!=0&&j.link!=2*n+1)
                    printf("%d ", j.link);
            }
        }
        printf("
    ");
        return 0;    
    } 
    
    
  • 相关阅读:
    terminal
    变量提升、函数提升
    ssh传输文件
    mocha测试框架
    npm-run 自动化
    webpack
    浅析babel
    构建工具gulp
    C++中TRACE宏及assert()函数的使用
    memcpy函数-C语言
  • 原文地址:https://www.cnblogs.com/cherrypill/p/13232606.html
Copyright © 2011-2022 走看看