网络流总结

15 篇文章 0 订阅


POJ - 1459 Power Network (裸最大流)


Power Network
Time Limit: 2000MS Memory Limit: 32768K
Total Submissions: 28550 Accepted: 14796

Description

A power network consists of nodes (power stations, consumers and dispatchers) connected by power transport lines. A node u may be supplied with an amount s(u) >= 0 of power, may produce an amount 0 <= p(u) <= p max(u) of power, may consume an amount 0 <= c(u) <= min(s(u),c max(u)) of power, and may deliver an amount d(u)=s(u)+p(u)-c(u) of power. The following restrictions apply: c(u)=0 for any power station, p(u)=0 for any consumer, and p(u)=c(u)=0 for any dispatcher. There is at most one power transport line (u,v) from a node u to a node v in the net; it transports an amount 0 <= l(u,v) <= l max(u,v) of power delivered by u to v. Let Con=Σ uc(u) be the power consumed in the net. The problem is to compute the maximum value of Con. 

An example is in figure 1. The label x/y of power station u shows that p(u)=x and p max(u)=y. The label x/y of consumer u shows that c(u)=x and c max(u)=y. The label x/y of power transport line (u,v) shows that l(u,v)=x and l max(u,v)=y. The power consumed is Con=6. Notice that there are other possible states of the network but the value of Con cannot exceed 6. 

Input

There are several data sets in the input. Each data set encodes a power network. It starts with four integers: 0 <= n <= 100 (nodes), 0 <= np <= n (power stations), 0 <= nc <= n (consumers), and 0 <= m <= n^2 (power transport lines). Follow m data triplets (u,v)z, where u and v are node identifiers (starting from 0) and 0 <= z <= 1000 is the value of l max(u,v). Follow np doublets (u)z, where u is the identifier of a power station and 0 <= z <= 10000 is the value of p max(u). The data set ends with nc doublets (u)z, where u is the identifier of a consumer and 0 <= z <= 10000 is the value of c max(u). All input numbers are integers. Except the (u,v)z triplets and the (u)z doublets, which do not contain white spaces, white spaces can occur freely in input. Input data terminate with an end of file and are correct.

Output

For each data set from the input, the program prints on the standard output the maximum amount of power that can be consumed in the corresponding network. Each result has an integral value and is printed from the beginning of a separate line.

Sample Input

2 1 1 2 (0,1)20 (1,0)10 (0)15 (1)20
7 2 3 13 (0,0)1 (0,1)2 (0,2)5 (1,0)1 (1,2)8 (2,3)1 (2,4)7
         (3,5)2 (3,6)5 (4,2)7 (4,3)5 (4,5)1 (6,0)5
         (0)5 (1)2 (3)2 (4)1 (5)4

Sample Output

15
6

Hint

The sample input contains two data sets. The first data set encodes a network with 2 nodes, power station 0 with pmax(0)=15 and consumer 1 with cmax(1)=20, and 2 power transport lines with lmax(0,1)=20 and lmax(1,0)=10. The maximum value of Con is 15. The second data set encodes the network from figure 1.

Source


题目大意:一共有n个点,有np个生产点,有nc个消费点,有m条有向边,问最多消费者会购买多少单位的东西。接下来m个三元组,表示有从点u到点v最多能够运输w个单位的东西,再接下来np个二元组,表示生产点u能够生产w个单位的东西,同样,再接下来nc个二元组,表示消费点u能够买w个单位的东西。


思路:


1、首先对m条边,直接对从u到v建边。


2、然后对于np个生产点,用一个超级源点连接这些个生产点,其边权值为生产点最多能够生产的物品单位数量。接着对于nc个消费点,将其连入一个超级汇点,每条边权值为消费点能够购买的物品单位数量。


3、这时候源点S也有了,汇点T也有了,直接跑一遍最大流算法即可。


这题每个点有明确的分工, 有的只生产,有的只输出, 跟二分图那样似的, 所以这样只把入流跟源点连起来, 出流跟汇点连起来...有些图要拆点,一般指的是没有明确的入流出流, 中间管道的权值是点的权值,所以这样的要拆点,入点跟出点 形成一个“水管”, 容量就是这个点的权值  这题就是这样的 -〉戳我


#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
using namespace std;
const int INF = 1e9;
const int maxn = 105;
const int maxv = 2e4;
int head[maxv], cur[maxv], d[maxv], s, t, k, sum;
int n, np, nc, m;
struct node
{
    int v, w, next;
}edge[maxv+6*maxn];
void addEdge(int u, int v, int w)
{
    edge[k].v = v;
    edge[k].w = w;
    edge[k].next = head[u];
    head[u] = k++;
    edge[k].v = u;
    edge[k].w = 0;
    edge[k].next = head[v];
    head[v] = k++;

}
int bfs()
{
    memset(d, 0, sizeof(d));
    d[s] = 1;
    queue<int> q;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front();
        if(u == t) return 1;
        q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            int to = edge[i].v, w = edge[i].w;
            if(w && d[to] == 0)
            {
                d[to] = d[u] + 1;
                if(to == t) return 1;
                q.push(to);
            }
        }
    }
    return 0;
}
int dfs(int u, int maxflow)
{
    if(u == t) return maxflow;
    int ret = 0;
    for(int i = cur[u]; i != -1; i = edge[i].next)
    {
        int to = edge[i].v, w = edge[i].w;
        if(w && d[to] == d[u]+1)
        {
            int f = dfs(to, min(maxflow-ret, w));
            edge[i].w -= f;
            edge[i^1].w += f;
            ret += f;
            if(ret == maxflow) return ret;
        }
    }
    return ret;
}
int Dinic()
{
    int ans = 0;
    while(bfs() == 1)
    {
        memcpy(cur, head, sizeof(head));
        ans += dfs(s, INF);
    }
    return ans;
}
int main()
{
    while(~scanf("%d%d%d%d", &n, &np, &nc, &m))
    {
        s = n+1, k = 0, t = n+2;
        memset(head, -1, sizeof(head));
        int x, y, z;
        while(m--)
        {
            scanf(" (%d,%d)%d", &x, &y, &z);
            addEdge(x, y, z);
        }
//        cout << 1 << endl;
        while(np--)
        {
            scanf(" (%d)%d", &x, &z);
            addEdge(s, x, z);
        }
        while(nc--)
        {
            scanf(" (%d)%d", &x, &z);
            addEdge(x, t, z);
        }
        printf("%d\n", Dinic());
    }
    return 0;
}

51nod  1442

 收藏
 关注

在某个国家有n个城市,他们通过m条无向的道路相连。每个城市有一支军队。第i个城市的军队有ai个士兵。现在士兵开始移动。每个士兵可以呆在原地,或者走到和他所在城市直接相邻的城市,即设每一条边长度为1的话,他离开之后不能距离原来城市大于1。

判断移动之后,能不能使得第i个城市恰好有bi个士兵。


Input
单组测试数据。
第一行有两个整数n和m(1 ≤ n ≤ 100, 0 ≤ m ≤ 200)。
第二行包含n个整数 a1, a2, ..., an (0 ≤ ai ≤ 100)。
第三行包含n个整数b1, b2, ..., bn (0 ≤ bi ≤ 100)。
接下来m行,每行给出两个整数 p 和q (1 ≤ p, q ≤ n, p ≠ q),表示p和q之间有一条无向的道路。
每两个城市之间最多有一条无向的道路。
Output
如果能够调整成功,输出YES,否则输出NO。
Input示例
4 4
1 2 6 3
3 5 3 1
1 2
2 3
3 4
4 2
Output示例
YES
System Message  (题目提供者)

题意:
给定n个顶点m条边的无向图,每个点上有若干名士兵,每名士兵只能留在原地或移动到相邻点上,给出移动后n个顶点的士兵人数,问是否存在一种移动方案.


分析:
建图,对i=1,2,...,n,源点S向标号i的点连容量为a[i]的边,标号i+n的点向汇点T连容量为b[i]的边,标号i的点向标号i+n的点连容量为INF的边
(表示留在原地),若原图中i,j两点间有边相连,则标号i的点向标号j+n的点连容量为INF的边(表示可以从i到j),标号j的点向标号i+n的点连容
量为INF的边(表示可以从j到i),跑一次从S到T的最大流,如果最大流既与所有a[i]之和相等,又与所有b[i]之和相等,则有解。

总结:

发现题目一旦像这种有一堆点的起始状态,然后是目标状态的,并且可以用路径来更改每个点的状态的。这种题用最大网络流做准没错。这题拆点,不是像之前做的那种, 点有权值,边也有权值,把点拆开,当边。这题是拆点代表这个点可以到自己, 到相邻的点,是连得相邻边的出边,这样就是只能到达相邻边了~

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
const int INF = 1e9;
const int maxn = 105;
const int maxv = 2e4;
int head[maxv], cur[maxv], d[maxv], s, t, k, sum, a[maxn], b[maxn];
int n, m;
int init()
{
    s = 0, t = n*2+1, k = 0;
    memset(head, -1, sizeof(head));
}
struct node
{
    int v, w, next;
}edge[maxv+6*maxn];
void addEdge(int u, int v, int w)
{
    edge[k].v = v;
    edge[k].w = w;
    edge[k].next = head[u];
    head[u] = k++;
    edge[k].v = u;
    edge[k].w = 0;
    edge[k].next = head[v];
    head[v] = k++;


}
int bfs()
{
    memset(d, 0, sizeof(d));
    d[s] = 1;
    queue<int> q;
    q.push(s);
    while(!q.empty())
    {
        int u = q.front();
        if(u == t) return 1;
        q.pop();
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            int to = edge[i].v, w = edge[i].w;
            if(w && d[to] == 0)
            {
                d[to] = d[u] + 1;
                if(to == t) return 1;
                q.push(to);
            }
        }
    }
    return 0;
}
int dfs(int u, int maxflow)
{
    if(u == t) return maxflow;
    int ret = 0;
    for(int i = cur[u]; i != -1; i = edge[i].next)
    {
        int to = edge[i].v, w = edge[i].w;
        if(w && d[to] == d[u]+1)
        {
            int f = dfs(to, min(maxflow-ret, w));
            edge[i].w -= f;
            edge[i^1].w += f;
            ret += f;
            if(ret == maxflow) return ret;
        }
    }
    return ret;
}
int Dinic()
{
    int ans = 0;
    while(bfs() == 1)
    {
        memcpy(cur, head, sizeof(head));
        ans += dfs(s, INF);
    }
    return ans;
}
int main()
{
    int x, y, sum1 = 0, sum2 = 0;
    scanf("%d%d", &n, &m);
    init();
    for(int i = 1; i <= n; i++) scanf("%d", &a[i]), addEdge(s, i, a[i]), sum1 += a[i], addEdge(i, i+n, INF);
    for(int i = 1; i <= n; i++) scanf("%d", &b[i]), addEdge(i+n, t, b[i]), sum2 += b[i];
    for(int i = 1; i <= m; i++)
    {
        scanf("%d%d", &x, &y);
        addEdge(x, y+n, INF);
        addEdge(y, x+n, INF);
    }
    if(sum1 != sum2)
        puts("NO");
    else
    {
        int tmp = Dinic();
        if(tmp != sum1)
            puts("NO");
        else
            puts("YES");
    }
    return 0;
}


51nod 1757 大灾变

 收藏
 关注
死亡之翼降临了!艾泽拉斯大陆的子民们必须逃出他的魔爪!
艾泽拉斯的结构是一棵树,这棵树上的一些节点是地精建造的通往地下避难所的洞口。
除了这些洞口之外,树上的每个节点上都有一个种族,每个种族通过树上的一条边都需要一个单位时间。
因为地精比较矮小,所以洞口很窄,每个单位时间只能让一个种族通过,但是一个单位时间内的一个节点上可以存在多个种族。
地精们需要你求出最少需要多少单位时间才能让所有种族躲进地下避难所。
【注意题目有修改,洞口不一定是叶子节点】
Input
第1行两个整数n(n<=2000)和m(m<=40)表示节点个数和洞口个数
接下来n-1行每行两个整数表示树上的每一条边
第n+1行m个整数表示所有洞口的编号,保证洞口是叶子节点
Output
一个整数t表示让所有种族躲进地下避难所的最少时间
Input示例
6 2
1 2
1 3
1 4
1 5
5 6
3 6
Output示例
3

题解:

首先二分答案,可以看出这是个很裸的网络流模型,但是问题在于怎么建模

假设二分出来的答案是mid,第一种建模方法:
S向所有非洞口节点连边流量为1,所有非洞口节点向距离<=mid的洞口节点连边流量为1,所有洞口节点向T连边流量为mid
然而这样做明显不行,因为没有考虑到一个洞口单位时间内只能通过一个种族所以会错误答案
因此第二种建模方法:
首先把所有洞口拆成mid个节点
S向所有非洞口节点连边流量为1,所有非洞口节点向距离<=mid的洞口拆成的第dis到第mid个节点连边流量为1,所有洞口拆成的所有节点向T连边流量为1
看起来正确性没问题了但是边数巨大,所以还是不能这样建模
于是正确的建模方法:
还是把所有洞口拆成mid个节点
S向所有非洞口节点连边流量为1,所有非洞口节点向距离<=mid的洞口拆成的第dis个节点连边流量为1,所有洞口拆成的所有节点向T连边流量为1,每个洞口拆成的第i个节点向第i+1个节点连边流量为正无穷
这样就可以通过这一题啦
当然其实还可以贪心做
每次找到一个度数为1(不包括连向洞口的边的度数)的离最近洞口最远的节点,然后把这个非洞口节点配对上这个洞口,贪心做n-m次,这样做正确性十分高
【因此我就把这个贪心卡掉了→_→】

总结:以前做过几个奶牛的, 也是从一些东西要去另一些地方, 但是那个没有什么要求, 就是floyd一下, 然后二分答案, 把所有距离小于答案的都连上边, 这样就好了,这题有个要求, 到洞口只能一个一个进, 这样就比较有趣了, 做法就是把洞口拆点。

但是这个题卡时, 让我知道了网络流模板哪里可以改很多。。以后大量数据的时候,都可以用下面的方法

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <queue>
using namespace std;
const int INF = 1e9;
const int maxn = 2e3 + 7;
const int maxv = 2e6;
int head[maxv], cur[maxv], d[maxv], s, t, k, sum, k1, head1[maxv], book[maxn], a[maxn];
int n, m, dis[50][maxn];
void init(int x)
{
    s = 0, t = n+m*x+1, k = 0;
    for(int i = 0; i <= t; i++) head[i] = -1;
}
struct node
{
    int v, w, next;
}edge[maxv];
struct node1
{
    int v, next;
}v[maxv];
void addEdge(int u, int v, int w)
{
    edge[k].v = v;
    edge[k].w = w;
    edge[k].next = head[u];
    head[u] = k++;
    edge[k].v = u;
    edge[k].w = 0;
    edge[k].next = head[v];
    head[v] = k++;

}
void addEdge1(int u, int to)
{
    v[k1].v = to;
    v[k1].next = head1[u];
    head1[u] = k1++;
}
int Q[maxv];
int bfs()
{
    for(int i = 0; i <= t; i++) d[i] = 0; //这里可以手动赋值
    d[s] = 1;
    int frnt = 0, rear = 0;  //手写队列比stl快400ms
    Q[rear++] = s;  
    while(frnt != rear)
    {
        int u = Q[frnt++];
        if(u == t) return 1;
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            int to = edge[i].v, w = edge[i].w;
            if(w && d[to] == 0)
            {
                d[to] = d[u] + 1;
                if(to == t) return 1;
                Q[rear++] = to;
            }
        }
    }
    return 0;
}
int dfs(int u, int maxflow)
{
    if(u == t || !maxflow) return maxflow;
    int ret = 0;
    for(int& i = cur[u]; i != -1; i = edge[i].next)  //这里!!!i前面那个&一定要加上。。差了好几s
    {
        int to = edge[i].v, w = edge[i].w;
        if(w && d[to] == d[u]+1)
        {
            int f = dfs(to, min(maxflow-ret, w));
            edge[i].w -= f;
            edge[i^1].w += f;
            ret += f;
            if(ret == maxflow) return ret;
        }
    }
    return ret;
}
int Dinic()
{
    int ans = 0;
    while(bfs() == 1)
    {
        for(int i = 0; i <= t; i++)  //这里。。。我用memcpy就t, 很慢很慢,一直t, 手动赋值就贼快。。
            cur[i] = head[i];
        ans += dfs(s, INF);
    }
    return ans;
}
void get_dist(int id, int x, int f)
{
    dis[id][x] = dis[id][f]+1;
    for(int i = head1[x]; i != -1; i = v[i].next)
    {
        int to = v[i].v;
        if(to == f) continue;
        get_dist(id, to, x);
    }
}
int check(int x)
{
    init(x);
    for(int i = 1; i <= n; i++)
        if(!book[i])
            addEdge(s, i, 1);
    for(int i = 1; i <= m; i++)
    {
        for(int j = 1; j <= x; j++)
            addEdge(n+(i-1)*x+j, t, 1);
        for(int j = 1; j < x; j++)
            addEdge(n+(i-1)*x+j, n+(i-1)*x+j+1, INF);
    }
    for(int i = 1; i <= n; i++)
    {
        if(!book[i])
        {
            for(int j = 1; j <= m; j++)
            {
                if(dis[j][i] <= x)
                    addEdge(i, n+(j-1)*x+dis[j][i], 1);
            }
        }
    }
    int res = Dinic();
    return res == n-m;
}
int main()
{
    int x, y;
    k1 = 0;
    scanf("%d%d", &n, &m);
    memset(head1, -1, sizeof(head1));  //这改成for也是对的
    for(int i = 1; i <  n; i++)
    {
        scanf("%d%d", &x, &y);
        addEdge1(x, y);
        addEdge1(y, x);
    }
    for(int i = 1; i <= m; i++)
    {
        scanf("%d", &a[i]);
        book[a[i]] = 1;
    }
    for(int i = 1; i <= m; i++)
        dis[i][0] = -1, get_dist(i, a[i], 0);
    int l = 0, r = n+1, mid, ans = 0;
    while(l <= r)
    {
        mid = (l+r)>>1;
        if(check(mid))
            ans = mid, r = mid - 1;
        else
            l = mid + 1;
    }
    printf("%d\n", ans);
    return 0;
}

计蒜客-Our Journey of Dalian Ends 2017 ACM-ICPC 亚洲区(乌鲁木齐赛区)网络赛(最小费用最大流)


题意:

给定若干个城市,出发点为大连,目的地为西安,但是要求中途必须经过上海,并且图中每个城市只能经过一次,给出m条路(双向道路),走第i条路需要wi代价,求所有满足要求的方案中花费的最小代价,如果没有满足的方案,输出-1。

思路:

相当于求从大连到上海加上西安到上海花费的代价总和最小,点只可走一次,所以进行拆点,所以是最小费用最大流,最小费用最大流有不少是用来解决在一定限制下取得最大最小利益的, 不相交,只能走几次这类就更明显了,每个点只能走一次,上海可以走两次,那就其余点都拆点,流量是1, 上海拆点流量是2.源点连到西安跟大连的入点,流量是1,费用是0, 上海连到源点,流量是2,费用是0,这样求的就是流量从西安大连出发到上海结束的最大流量,在这基础上求得最小费用。


#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#include <vector>
#include <map>
#include <algorithm>
using namespace std;
const int maxn = 4e4 + 7;
const int maxe = 200005;
const int INF = 1e9;
int head[maxe], dis[maxn], path[maxn], pre[maxn], book[maxn] , n, m, s, t, k, sum, index;
map<string, int> id;
char s1[maxn], s2[maxn];
struct node
{
    int v, w, f, next, cnt;
}edge[3000005];
void addEdge(int u, int v, int f, int w)
{
    edge[k].v = v;
    edge[k].w = w;
    edge[k].f = f;
    edge[k].cnt = k;
    edge[k].next = head[u];
    head[u] = k++;
    edge[k].v = u;
    edge[k].w = -w;
    edge[k].f = 0;
    edge[k].cnt = k;
    edge[k].next = head[v];
    head[v] = k++;
}
void init()
{
    s = 0, t = n*4+1, k = 0, index = 1;
    memset(head, -1, sizeof(head));
    id.clear();
}
int spfa()
{
    queue<int> q;
    q.push(s);
    memset(pre, -1, sizeof(pre));
    memset(path, -1, sizeof(path));
    for(int i = 1; i <= t; i++) dis[i] = INF;
    dis[s] = 0;
    memset(book, 0, sizeof(book));
    book[s] = 1;
    while(!q.empty())
    {
        int u = q.front();
        q.pop();
        book[u] = 0;
        for(int i = head[u]; i != -1; i = edge[i].next)
        {
            int to = edge[i].v;
            int w = edge[i].w;
            int f = edge[i].f;
            if(f && dis[to] > dis[u] + w)
            {
//                cout << u << ' ' << to << endl;
                dis[to] = dis[u] + w;
                pre[to] = u;
                path[to] = edge[i].cnt;
                if(!book[to])
                {
                    q.push(to);
                    book[to] = 1;
                }
            }
        }
    }
//    cout << dis[t] << endl;
    if(dis[t] == INF) return 0;
    else return 1;
}
int Min_costflow()
{
    int ans = 0;
    int maxflow = 0;
    while(spfa())
    {
        int minx = INF;
        for(int i = t; i != s; i = pre[i])
        {
            minx = min(minx, edge[path[i]].f);
        }
        maxflow += minx;
        ans += dis[t]*minx;
//        cout << ans << endl;
        for(int i = t; i != s; i = pre[i])
        {
            edge[path[i]].f -= minx;
            edge[path[i]^1].f += minx;
        }
    }
    if(maxflow != 2) ans = -1;
    return ans;
}
void check(char *s)
{
    if(id[s] == 0) id[s] = index++;
}
int main()
{
    int _;
//    freopen("in.txt","r",stdin);
    cin >> _;
    while(_--)
    {
        scanf("%d", &n);
        init();
        int x, sh, xa, dl;
        for(int i = 1; i <= n; i++)
        {
            scanf(" %s", s1);
            check(s1);
            scanf(" %s", s2);
            check(s2);
            scanf("%d", &x);
            if(strcmp(s1, "Shanghai") == 0)
                sh = id[s1];
            if(strcmp(s2, "Shanghai") == 0)
                sh = id[s2];
            if(strcmp(s1, "Xian") == 0)
                xa = id[s1];
            if(strcmp(s2, "Xian") == 0)
                xa = id[s2];
            if(strcmp(s1, "Dalian") == 0)
                dl = id[s1];
            if(strcmp(s2, "Dalian") == 0)
                dl = id[s2];
            addEdge(id[s1]+n*2, id[s2], 1, x);
            addEdge(id[s2]+n*2, id[s1], 1, x);
        }
        for(int i = 1; i < index; i++)
        {
            if(i == sh) addEdge(i , i+n*2, 2, 0);
            else
                addEdge(i, i+n*2, 1, 0);
        }
        addEdge(s, dl, 1, 0);
        addEdge(s, xa, 1, 0);
        addEdge(sh+n*2, t, 2, 0);
        int ans = Min_costflow();
        printf("%d\n", ans);
    }
    return 0;
}
/*
3
2
Dalian Shanghai 3
Shanghai Xian 4
5
Dalian Shanghai 7
Shanghai Nanjing 1
Dalian Nanjing 3
Nanjing Xian 5
Shanghai Xian 8
3
Dalian Nanjing 6
Shanghai Nanjing 7
Nanjing Xian 8
*/




  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值