HDU 4067 Random Maze 最小费用最大流

来源:互联网 发布:linux shell 数值计算 编辑:程序博客网 时间:2024/06/06 15:38


Random Maze

Time Limit: 10000/3000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)
Total Submission(s): 985    Accepted Submission(s): 334


Problem Description
In the game “A Chinese Ghost Story”, there are many random mazes which have some characteristic:
1.There is only one entrance and one exit.
2.All the road in the maze are unidirectional.
3.For the entrance, its out-degree = its in-degree + 1.
4.For the exit, its in-degree = its out-degree + 1.
5.For other node except entrance and exit, its out-degree = its in-degree.

There is an directed graph, your task is removing some edge so that it becomes a random maze. For every edge in the graph, there are two values a and b, if you remove the edge, you should cost b, otherwise cost a.
Now, give you the information of the graph, your task if tell me the minimum cost should pay to make it becomes a random maze.

 

Input
The first line of the input file is a single integer T.
The rest of the test file contains T blocks.
For each test case, there is a line with four integers, n, m, s and t, means that there are n nodes and m edges, s is the entrance's index, and t is the exit's index. Then m lines follow, each line consists of four integers, u, v, a and b, means that there is an edge from u to v.
2<=n<=100, 1<=m<=2000, 1<=s, t<=n, s != t. 1<=u, v<=n. 1<=a, b<=100000
 

Output
For each case, if it is impossible to work out the random maze, just output the word “impossible”, otherwise output the minimum cost.(as shown in the sample output)
 

Sample Input
22 1 1 22 1 2 35 6 1 41 2 3 12 5 4 55 3 2 33 2 6 72 4 7 63 4 10 5
 

Sample Output
Case 1: impossibleCase 2: 27


思路想到一半多就不行了,唉,弱爆了,得加强训练了,今年的网络赛。。。

参考某大牛的思路:

看到这些条件首先想到的是欧拉路,如果我们人为的添加一条t->s的边,满足条件的不就是欧拉图么?所以应该可以用spfa不断消负环来解决,但是这样很慢,于是考虑费用流。

建图:如果人为添加t->s的边,那么图中所有顶点要满足的条件都是一样的了,我们以此为目的来建图。

对于每条边,我们只有两种操作,要么保留要么删除,那么先假设两种操作都能满足条件,我们就可以选择花费小的操作来执行,最后再根据实际情况调整。

首先不加入任何边,在添加或删除(不加入)边的过程中,对每个顶点v记录in[v]为其当前入度,out[v]为其出度,sum为当前的总花费。

那么对于每条边,如果a<=b,那么保留这条边,in[v]++,out[u]++,sum+=a,然后连边v->u,流量1,费用为b-a。
如果b<a,那么删去这条边,in[u]++,out[v]++,sum+=b,然后连边u->v,流量1,费用为a-b。
然后我们人为的加入一条t->s,直接in[s]++,out[t]++,使得图中所有点处于相同的状况。
设立超级源汇S、T,对于原图的每个点i,如果in[i]>out[i],则连边S->i,流量为1,费用为in[i]-out[i],否则连边i->T,流量为1,费用为out[i]-in[i],至此,建图完成。

现在求S到T的费用流mincost,然后检查从S发出的边,如果全部满流则有解,答案就是sum+mincost,否则无解。

这样建图的意义:例如对点i,in[i]>out[i],说明当前该点入度大于出度,那么我们把之前删除的以i为起点的边添加回来 或者把之前保留的以i为终点的边删除,现在边的费用其实是改变边状态所需要额外付的费用,而最小费用流所求的就是全部调整的总费用了,于是答案就是sum(初始操作的费用)+mincost(额外付出的费用)。


#include <cstdio>#include <cstdlib>#include <cstring>#include <iostream>#include <algorithm>#include <queue>using namespace std;const int MAXN = 222;const int MAXM = MAXN*MAXN+10;const int INF = 0x3f3f3f3f;struct Edge{    int v, cap, cost, next;    Edge() {}    Edge(int t_v, int t_cap, int t_cost, int t_next) : v(t_v), cap(t_cap), cost(t_cost), next(t_next) {}}edge[MAXM];int n, m, vs, vt, NE;int head[MAXN];int dist[MAXN], pre[MAXN], cur[MAXN];bool mark[MAXN];int indegree[MAXN], outdegree[MAXN];inline int min(int a, int b){    return a < b ? a : b;}inline int max(int a, int b){    return a > b ? a : b;}void Init(){    NE = 0;    memset(head, -1, sizeof(head));    memset(indegree, 0, sizeof(indegree));    memset(outdegree, 0, sizeof(outdegree));}void addEdge(int u, int v, int cap, int cost){    edge[NE].v = v;    edge[NE].cap = cap;    edge[NE].cost = cost;    edge[NE].next = head[u];    head[u] = NE++;    edge[NE].v = u;    edge[NE].cap = 0;    edge[NE].cost = -cost;    edge[NE].next = head[v];    head[v] = NE++;}bool SPFA(int vs, int vt){    memset(mark, false, sizeof(mark));    fill(dist, dist + MAXN - 1, INF);    dist[vs] = 0;    queue<int> que;    que.push(vs);    while(!que.empty())    {        int u = que.front(); que.pop(); mark[u] = false;        for(int i = head[u]; i != -1; i = edge[i].next)        {            int v = edge[i].v, cost = edge[i].cost;            if(edge[i].cap > 0 && dist[u]+cost < dist[v])            {                dist[v] = cost + dist[u];                pre[v] = u;                cur[v] = i;                if(!mark[v])                {                    mark[v] = true;                    que.push(v);                }            }        }    }    return dist[vt] < INF;}int MinCostMaxFlow(int vs, int vt){    int flow = 0, cost = 0;    while(SPFA(vs, vt))    {        int aug = INF;        for(int i = vt; i != vs; i = pre[i])        {            aug = min(aug, edge[cur[i]].cap);        }        flow += aug; cost += dist[vt] * aug;        for(int i = vt; i != vs; i = pre[i])        {            edge[cur[i]].cap -= aug;            edge[cur[i]^1].cap += aug;        }    }    return cost;}bool judge(){    for(int i = head[vs]; i != -1; i = edge[i].next)    {        if(edge[i].cap > 0)            return false;    }    return true;}int main(){    //freopen("aa.in", "r", stdin);    //freopen("bb.out", "w", stdout);    int T;    int u, v, a, b, s, t, sum, cost;    int kcase = 0;    scanf("%d", &T);    while(T--)    {        kcase++;        scanf("%d %d %d %d", &n, &m, &s, &t);        Init(); sum = 0; vs = 0, vt = n + 1;        while(m--)        {            scanf("%d %d %d %d", &u, &v, &a, &b);            if(a <= b)            {                sum += a;                indegree[v]++, outdegree[u]++;                addEdge(v, u, 1, b - a);            }            else            {                sum += b;                addEdge(u, v, 1, a - b);            }        }        indegree[s]++, outdegree[t]++;        for(int i = 1; i <= n; ++i)        {            if(indegree[i] > outdegree[i])            {                addEdge(0, i, indegree[i] - outdegree[i], 0);            }            if(outdegree[i] > indegree[i])            {                addEdge(i, n + 1, outdegree[i] - indegree[i], 0);            }        }        cost = MinCostMaxFlow(vs, vt);        if(judge())        {            printf("Case %d: %d\n", kcase, sum + cost);        }        else        {            printf("Case %d: impossible\n", kcase);        }    }    return 0;}