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

46 篇文章 0 订阅

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
 

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;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值