River Problem (hdu 3947 流量等式建图 难题 最小费用最大流)

River Problem

Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)

Total Submission(s): 515    Accepted Submission(s): 209

Problem Description

The River of Bitland is now heavily polluted. To solve this problem, the King of Bitland decides to use some kinds of chemicals to make the river clean again.

The structure of the river contains n nodes and exactly n-1 edges between those nodes. It‘s just the same as all the rivers in this world: The edges are all unidirectional to represent water flows. There are source points, from which the water flows, and there
is exactly one sink node, at which all parts of the river meet together and run into the sea. The water always flows from sources to sink, so from any nodes we can find a directed path that leads to the sink node. Note that the sink node is always labeled
1.

As you can see, some parts of the river are polluted, and we set a weight Wi for each edge to show how heavily polluted this edge is. We have m kinds of chemicals to clean the river. The i-th chemical can decrease the weight for all edges in the path from Ui
to Vi by exactly 1. Moreover, we can use this kind of chemical for Li times, the cost for each time is Ci. Note that you can still use the chemical even if the weight of edges are 0, but the weight of that edge will not decrease this time.

When the weight of all edges are 0, the river is cleaned, please help us to clean the river with the least cost.

Input

The first line of the input is an integer T representing the number of test cases. The following T blocks each represents a test case.

The first line of each block contains a number n (2<=n<=150) representing the number of nodes. The following n-1 lines each contains 3 numbers U, V, and W, means there is a directed edge from U to V, and the pollution weight of this edge is W. (1<=U,V<=n, 0<=W<=20)

Then follows an number m (1<=m<=2000), representing the number of chemical kinds. The following m lines each contains 4 numbers Ui, Vi, Li and Ci (1<=Ui,Vi<=n, 1<=Li<=20, 1<=Ci<=1000), describing a kind of chemical, as described above. It is guaranteed that
from Ui we can always find a directed path to Vi.

Output

First output "Case #k: ", where k is the case numbers, then follows a number indicating the least cost you are required to calculate, if the answer does not exist, output "-1" instead.

Sample Input

2
3
2 1 2
3 1 1
1
3 1 2 2
3
2 1 2
3 1 1
2
3 1 2 2
2 1 2 1

Sample Output

Case #1: -1
Case #2: 4

Author

Thost & Kennethsnow

Source

2011 Multi-University Training Contest 11 - Host by
UESTC

Recommend

xubiao

题意:治理河水污染问题,有n个点,n-1条有向边表示河流,每条河流都会汇聚到1,现在告诉每条河流的污染值w,另外有m种药水,每种药水可以治理起点为u终点为v的河水段,可以使该河段的污染值减少1,该种药水最多可以用L次,花费为c,问把污染治理完最小需要的花费是多少。

做这一题时参考了NOI 2008 志愿者招募 employee,我是看了这位大神的博客才懂得,建议先看这个Here

代码:

/*
思路:该题需要构建网络,用最小费用流求解。这一题花了整整一天的时间才想明白,模型隐藏得在太深了,关键在于根据流量不等式建图。

下面具体说一下建图过程。

这种题型有一个特点,就是某一个因素影响着连续的若干个位置,那么就要从中抽象出流量不等式根据流量平衡建图。

对于其中的一条边u->v( 这条边的边号为 j ,污染值为w[j] ),假设共有i种药的治理河段区间包含了这条边,也就是这i种药可以

拿来治理u->v这条河道,设这i种药使用的次数分别为Xj[1],Xj[2]...Xj[i],

则存在不等式
        Xj[1]+Xj[2]+...+Xj[i]>=w[j]      --(a)
    成立。
这里我们引入一个增量Y[j],将(a)简单变形得
        Xj[1]+Xj[2]+...+Xj[i]-Y[j]=w[j]   --(b)
    显然成立。
同理,我们总共可以得到n-1个这样的不等式:
        P1 = X1[1]+X1[2]+...+X1[a]-Y[1]=w[1]     --(1)           //a种药
        P2 = X2[1]+X2[2]+...+X2[b]-Y[2]=w[2]     --(2)           //b种药
                    .                         .
                    .                         .
                    .                         .
        Pj = Xj[1]+Xj[2]+...+Xj[i]-Y[j]=w[j]     --(j)           //i种药
                    .                         .
                    .                         .
                    .                         .
        Pn-2 = Xn-2[1]+Xn-2[2]+...+Xn-2[d]-Y[n-2]=w[n-2]   --(n-2) //d种药
        Pn-1= Xn-1[1]+Xn-1[2]+...+Xn-1[e]-Y[n-1]=w[n-1]   --(n-1) //e种药
注意:这里a,b,i,d,e是表示该条边有多少种药经过

然后新增一个等式,也就是增加题目里的1->n+1这条边,即增加不等式:
        Pn = 0
这样,我们得到了n个等式,把这n个当作节点。

现在建边,将上面n个等式在原图中两两相邻的相减(就是在给的有向图中相邻的两条边),又得到一系列等式,加入上式(1)和(2)相邻,则(2)-(1)得:
        P2-P1 = X2[1]+X2[2]+...+X2[b]-X1[1]-X1[2]-...-X1[a]-Y[2]+Y[1] = w[2]-w[1]
    移项得:
        w[2]-w[1]-X2[1]-X2[2]-...-X2[b]+X1[1]+X1[2]+...+X1[a]+Y[2]-Y[1] = 0     --(3)
    这个等式左边有正有负,就好比网络流中节点流量的流入流出,右边为0符合流量平衡条件,所以可以根据这个不等式建边。

    怎么建?

    (1)添加源点S和汇点T。

    (2)如果一个等式左边有非负整数c(如上式(3)中c=w[2]-w[1]),从源点S向该等式对应的顶点连接一条容量为c(这里

        定义一个全局变量all+=c,统计从源点出来的流量之和,后续用来判断是否存在可行解),权值为0的有向边;如果一

        个等式左边为负整数c,从该等式对应的顶点向汇点T连接一条容量为-c,权值为0的有向边。

    (3)如果一个变量X[i]在第j个等式中出现为-X[i],在第k个等式中出现为X[i],从顶点j向顶点k连接一条容量为INF,权值为c[i]的有向边。

    (4)如果一个变量Y[i]在第j个等式中出现为-Y[i],在第k个等式中出现为Y[i],从顶点j向顶点k连接一条容量为INF,权值为0的有向边。

这样图建好以后,求从源点S到汇点T的最小费用流,若最小流flow==all,则存在解,输出费用,否则没有解,输出-1.

*/

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <string>
#include <map>
#include <stack>
#include <vector>
#include <set>
#include <queue>
#pragma comment (linker,"/STACK:102400000,102400000")
#define mod 1000000009
#define INF 100000
#define pi acos(-1.0)
#define eps 1e-6
#define lson rt<<1,l,mid
#define rson rt<<1|1,mid+1,r
#define FRE(i,a,b)  for(i = a; i <= b; i++)
#define FREE(i,a,b) for(i = a; i >= b; i--)
#define FRL(i,a,b)  for(i = a; i < b; i++)
#define FRLL(i,a,b) for(i = a; i > b; i--)
#define mem(t, v)   memset ((t) , v, sizeof(t))
#define sf(n)       scanf("%d", &n)
#define sff(a,b)    scanf("%d %d", &a, &b)
#define sfff(a,b,c) scanf("%d %d %d", &a, &b, &c)
#define pf          printf
#define DBG         pf("Hi\n")
typedef long long ll;
using namespace std;

const int MAXN = 10005;
const int MAXM = 1000000;

struct E        //用来存输入的那n-1条边,输入u->v,存的时候反向v->u,
{               //由题意知,最后都会汇聚到点1,我们反向建边便于dfs从1逆向访问其他点
    int u,v,w,next;
}e[MAXM];

int hed[MAXN],cnt;
int id[MAXN];   //id数组用来存边的编号,这里不用开二维的,因为仔细想想这是一个以1为根的树,但边的方向相反,所以从每个节点i出去的边只有一条
                //这样我们用id[i]就可以来唯一表示从i点出去的边了
struct Edge
{
    int to,next,cap,flow,cost;
}edge[MAXM];

int head[MAXN],tol;
int pre[MAXN],dis[MAXN];
bool vis[MAXN];
int N,n,all,m;

void init(int n)
{
    N=n;all=0;
    tol=0;cnt=0;
    memset(id,0,sizeof(id));
    memset(hed,-1,sizeof(hed));
    memset(head,-1,sizeof(head));
}

void ADD(int u,int v,int w)
{
    e[cnt].u=u;
    e[cnt].v=v;
    e[cnt].w=w;
    e[cnt].next=hed[u];
    hed[u]=cnt++;
}

void addedge(int u,int v,int cap,int cost)
{
    edge[tol].to=v;
    edge[tol].cap=cap;
    edge[tol].cost=cost;
    edge[tol].flow=0;
    edge[tol].next=head[u];
    head[u]=tol++;
    edge[tol].to=u;
    edge[tol].cap=0;
    edge[tol].cost=-cost;
    edge[tol].flow=0;
    edge[tol].next=head[v];
    head[v]=tol++;
}

bool spfa(int s,int t)
{
    queue<int>q;
    for (int i=0;i<N;i++)
    {
        dis[i]=INF;
        vis[i]=false;
        pre[i]=-1;
    }
    dis[s]=0;
    vis[s]=true;
    q.push(s);
    while (!q.empty())
    {
        int u=q.front();
        q.pop();
        vis[u]=false;
        for (int i=head[u];i!=-1;i=edge[i].next)
        {
            int v=edge[i].to;
            if (edge[i].cap > edge[i].flow && dis[v] > dis[u] + edge[i].cost)
            {
                dis[v]=dis[u] + edge[i].cost;
                pre[v]=i;
                if (!vis[v])
                {
                    vis[v]=true;
                    q.push(v);
                }
            }
        }
    }
    if (pre[t]==-1) return false;
    else return true;
}

//返回的是最大流,cost存的是最小费用
int minCostMaxflow(int s,int t,int &cost)
{
    int flow=0;
    cost=0;
    while (spfa(s,t))
    {
        int Min=INF;
        for (int i=pre[t];i!=-1;i=pre[edge[i^1].to])
        {
            if (Min > edge[i].cap-edge[i].flow)
                Min=edge[i].cap-edge[i].flow;
        }
        for (int i=pre[t];i!=-1;i=pre[edge[i^1].to])
        {
            edge[i].flow+=Min;
            edge[i^1].flow-=Min;
            cost+=edge[i].cost*Min;
        }
        flow+=Min;
    }
    return flow;
}

void dfs(int u,int pre_w)   //dfs从1遍历全图,u是当前节点,pre_w是上一条边的污染值
{
    int sum=0;
    for (int i=hed[u];~i;i=e[i].next)
    {
        int v=e[i].v;
        dfs(v,e[i].w);
        sum+=e[i].w;
        addedge(id[u],id[v],INF,0);
    }
    int cc=pre_w-sum;
    if (cc>0)
    {
        addedge(0,id[u],cc,0);
        all+=cc;
    }
    if (cc<0)
        addedge(id[u],n+1,-cc,0);
    return ;
}

int main()
{
#ifndef ONLINE_JUDGE
    freopen("C:/Users/asus1/Desktop/IN.txt","r",stdin);
#endif
    int i,j,T,u,v,w,l,c,cas=0;
    sf(T);
    while (T--)
    {
        sf(n);
        init(n+2);
        FRL(i,1,n)
        {
            sfff(u,v,w);
            ADD(v,u,w);      //把边的方向反过来建边
            id[u]=i;        //记录边号
        }

        ADD(n+1,1,0);       //因为所有河流都会汇聚到1,那么在原图中加一条1->n+1的边
        id[1]=n;

        dfs(1,0);

        sf(m);
        FRE(i,1,m)
        {
            scanf("%d%d%d%d",&u,&v,&l,&c);
            addedge(id[u],id[v],l,c);
        }

        int x,ans;
        x=minCostMaxflow(0,n+1,ans);
        pf("Case #%d: ",++cas);
        if (x==all)
            pf("%d\n",ans);
        else
            pf("-1\n");
    }
    return 0;
}

版权声明:本文为博主原创文章,未经博主允许不得转载。

时间: 2024-08-08 22:06:53

River Problem (hdu 3947 流量等式建图 难题 最小费用最大流)的相关文章

River Problem HDU - 3947(公式建边)

River Problem Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 721    Accepted Submission(s): 282 Problem Description The River of Bitland is now heavily polluted. To solve this problem, the King

HDU 3435 A new Graph Game(最小费用最大流)&amp;amp;HDU 3488

A new Graph Game Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1849    Accepted Submission(s): 802 Problem Description An undirected graph is a graph in which the nodes are connected by undir

HDU 3435 A new Graph Game(最小费用最大流)&amp;HDU 3488

A new Graph Game Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1849    Accepted Submission(s): 802 Problem Description An undirected graph is a graph in which the nodes are connected by undir

HDU 4862 Jump(更多的联合培训学校1)(最小费用最大流)

职务地址:pid=4862">HDU4862 最小费用流做的还是太少. 建图想不出来. . . 直接引用官方题解的话吧... 最小K路径覆盖的模型.用费用流或者KM算法解决,构造二部图,X部有N*M个节点.源点向X部每一个节点连一条边,流量1,费用0,Y部有N*M个节点,每一个节点向汇点连一条边.流量1,费用0.假设X部的节点x能够在一步之内到达Y部的节点y,那么就连边x->y,费用为从x格子到y格子的花费能量减去得到的能量.流量1,再在X部添加一个新的节点,表示能够从随意节点出发K

HDU 4862 Jump(多校联合训练1)(最小费用最大流)

题目地址:HDU4862 最小费用流做的还是太少.建图想不出来... 直接引用官方题解的话吧... 最小K路径覆盖的模型,用费用流或者KM算法解决,构造二部图,X部有N*M个节点,源点向X部每个节点连一条边,流量1,费用0,Y部有N*M个节点,每个节点向汇点连一条边,流量1,费用0,如果X部的节点x可以在一步之内到达Y部的节点y,那么就连边x->y,费用为从x格子到y格子的花费能量减去得到的能量,流量1,再在X部增加一个新的节点,表示可以从任意节点出发K次,源点向其连边,费用0,流量K,这个点向

HDU ACM 4494 Teamwork 最小费用最大流

题意:n个工作地,m种工人,工作地0是仓库,其他的都需要修缮,每个地点需要多个工种的工人若干,不同工种不能相互取代.每个工作地有一个开工时间,凑齐所有工人后准时开工,修缮也需要一定时间.一个工人可以在一个地方工作完后再到其他地方,两地直接的距离是欧几里得距离,可以算作时间.最少需要多少工人. 分析:只用费用流.每种工人不能相互替换,没有任何关系.因此对每个工种进行建图求解最小费用累加即可得到最终结果. 超级源点cs是仓库,超级汇点为ct. 一个地点拆成三个点,i.i'.i".k表示工种,对每个点

多校第一场:HDU 4862 最小费用最大流

思路:这题主要是建图搞了好久,以前没判断过满流,所以又看了这个知识点,然后才发现自己的最小费用最大流在求满流的时候有bug,正好改了过来. 建图:开始看题解知道这题是最小费用最大流,然后没看解释就做了.然后自己建的图没得求出答案,然后想了好久也没发现哪里错.然后看了官方题解,发现自己的建图和官方差太大了.可能是受昨天做POJ最小费用建图的影响吧.官方的建太符合题目意思了,只能说,我还看了好久建图才理解的. 构造二部图,X部有N*M个节点,源点向X部每个节点连一条边,流量1,费用0:Y部有N*M个

poj3680 Intervals 区间k覆盖问题 最小费用最大流 建图巧妙

/** 题目:poj3680 Intervals 区间k覆盖问题 最小费用最大流 建图巧妙 链接:http://poj.org/problem?id=3680 题意:给定n个区间,每个区间(ai,bi),以及权值wi.选出一些区间,满足权值和最大且任何一个点不会被超过k个区间覆盖. 思路: 建图:对于每个区间(ai,bi). ai->bi,cap = 1,cost = -wi; (离散化后的ai,bi) 所有区间的端点放到数组,进行从小到大排序,去重,离散化,在数组内相邻的u端点,v端点.u->

hdu4106 区间k覆盖问题(连续m个数,最多选k个数) 最小费用最大流 建图巧妙

/** 题目:hdu4106 区间k覆盖问题(连续m个数,最多选k个数) 最小费用最大流 建图巧妙 链接:http://acm.hdu.edu.cn/showproblem.php?pid=4106 题意:给你n个数,每连续m个数,最多选k个数,问可以选的数的权值和最大多少. 思路:可以转化为区间k覆盖问题.区间k覆盖问题是每个点最多被k个区间覆盖.本题是每个区间最多选k个点. 刚好相反.我的做法有点不同其他博客那种做法.当然本质一样. 我这里的i就是原来n个数的下标,现在作为图中该数的节点编号