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\n", 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\n", 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\n", 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\n",-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("\n");
    return 0;    
} 

posted @ 2020-07-03 20:52  Sakura_Momoko  阅读(192)  评论(0编辑  收藏  举报