HDU 4067 Random Maze 费用流

Random Maze


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

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

2 2
1 1 2 2 1 2 3 5 6 1 4 1 2 3 1 2 5 4 5 5 3 2 3 3 2 6 7 2 4 7 6 3 4 10
5

Sample
Output

Case
1: impossible Case 2: 27

Source

The 36th ACM/ICPC Asia Regional Fuzhou Site —— Online Contest

题目大意:

一个可行图需要满足以下条件:

1.所有的路都是单向的;

2.对于入口 : 出度 = 入度 +
1;

3.对于出口 : 入度 = 出口 +
1;

4.对于其他点 : 入度 = 出度;

给你一个有向图,n个点,m条边,起点S,终点T,每条边存在图中要花费a元,从图中删除要花费b元。问最少需要花费多少使得图成为一个可行图。

题目分析:

易知,如果是一个可行图,则添加T到S的边恰好是一个欧拉回路(所有点的出度 = 入度)。

怎样建图好呢?我们可以先贪心建图,将花费最小的情况构建出来,不管是否可行!

*对于每条边(u,v),如果a 
>  b,则建边(v,u,1,a - b),sum += b,默认这条边是不存在于图中的,由于不存在图中所以点的出度及入度不变化。

*对于每条边(u,v),如果a <=
b,则建边(u,v,1,b - a),sum += a,默认这条边是存在于图中的,且++deg[u], --deg[v],这里deg[u] = out[u] -
in[u]。

*对于S,T,连接(T,S),即++deg[T],
--deg[S](就是为了使得图中每个点的状况能够相同)。

至此,最小情况构建完毕,接下来我们该怎么才能使得整幅图符合条件?

首先,添加超级源s,超级汇t。

由构图方法可以知道,对于每个点u,如果deg[u]
> 0,说明从它出发的边大于指向它的边,建边(s,u,deg[u],0),输出流量,选择恢复指向它的边或者删除从它出发的边,流量用完则该点的出度 =
入度。对于每个点v,如果deg[v] <
0,说明从它出发的边少于指向它的边,建边(v,t,deg[v],0),接收流量,选择恢复从它出发的边或者删除指向它的边,全部接收则该点的出度 =
入度。

对于图中的每个点,流向它的边有两种情况,从它出发的边也有两种情况。当流向它的是原图的正向边时(即在原图中b
>= a),流过它的时候,相当于删除这条边。接下来从这个点出发,当流出的边是原图的正向边时(即在原图中b >=
a),相当于删除这条边,恰好与流进的边抵消,度保持不变;当流出的边是原图的反向边(即在原图中b <
a),相当于恢复了指向它的反向边,同样抵消了入边的影响。对于指向该点的是原图的反向边时(即在原图中b <
a)同理。对于出度大于入度的点,接收源点流出的流量相当于给它机会抵消多余的出度;对于入度大于出度的点,流量流入汇点相当于给它机会抵消多余的入度;对于中间点,由上述可知无论怎么流都不会影响他们的出入度平衡。

最后,只要跑一次最小费用最大流,如果满流(即流量等于多余的入度或者多余的出度),则有解,解为一开始存在于图中的费用sum+流量flow;否则无解。另外可知,如果有解,则所有流过的边都是需要恢复或者删除的边。

如果会用一些画图软件的话或许能描述的更清楚一些QUQ。。。

代码如下:

#include <stdio.h>
#include <string.h>
#include <algorithm>
#define min(a, b) ((a) < (b) ? (a) : (b))
#define REP(i, n) for(int i = 1; i <= n; ++i)
#define MS0(X) memset(X, 0, sizeof X)
#define MS1(X) memset(X, -1, sizeof X)
using namespace std;
const int maxE = 3000000;
const int maxN = 105;
const int oo = 0x3f3f3f3f;
struct Edge{
int v, c, w, n;
};
Edge edge[maxE];
int adj[maxN], l;
int d[maxN], cur[maxN], a[maxN];
int inq[maxN], Q[maxE], head, tail;
int cost, flow, s, t;
int n, m, S, T;
int deg[maxN];
void addedge(int u, int v, int c, int w){
edge[l].v = v; edge[l].c = c; edge[l].w = w; edge[l].n = adj[u]; adj[u] = l++;
edge[l].v = u; edge[l].c = 0; edge[l].w = -w; edge[l].n = adj[v]; adj[v] = l++;
}
int SPFA(){
memset(d, oo, sizeof d);
memset(inq, 0, sizeof inq);
head = tail = 0;
d[s] = 0;
a[s] = oo;
cur[s] = -1;
Q[tail++] = s;
while(head != tail){
int u = Q[head++];
inq[u] = 0;
for(int i = adj[u]; ~i; i = edge[i].n){
int v = edge[i].v;
if(edge[i].c && d[v] > d[u] + edge[i].w){
d[v] = d[u] + edge[i].w;
cur[v] = i;
a[v] = min(edge[i].c, a[u]);
if(!inq[v]){
inq[v] = 1;
Q[tail++] = v;
}
}
}
}
if(d[t] == oo) return 0;
flow += a[t];
cost += a[t] * d[t];
for(int i = cur[t]; ~i; i = cur[edge[i ^ 1].v]){
edge[i].c -= a[t];
edge[i ^ 1].c += a[t];
}
return 1;
}
int MCMF(){
flow = cost = 0;
while(SPFA());
return flow;
}
void work(){
int u, v, a, b, sum = 0;
scanf("%d%d%d%d", &n, &m, &S, &T);
MS1(adj);
MS0(deg);
l = 0;
while(m--){
scanf("%d%d%d%d", &u, &v, &a, &b);
if(a > b){
addedge(v, u, 1, a - b);
sum += b;
}
else{
addedge(u, v, 1, b - a);
sum += a;
++deg[u];
--deg[v];
}
}
++deg[T];
--deg[S];
s = 0; t = n + 1;
int nflow = 0;
REP(i, n){
if(deg[i] > 0){
addedge(s, i, deg[i], 0);
nflow += deg[i];
}
if(deg[i] < 0){
addedge(i, t, -deg[i], 0);
}
}
MCMF();
if(nflow == flow){
printf("%d\n", cost + sum);
}
else printf("impossible\n");
}
int main(){
int T, cas;
for(scanf("%d", &T), cas = 1; cas <= T; cas++){
printf("Case %d: ", cas);
work();
}
return 0;
}

HDU 4067

HDU 4067 Random Maze 费用流,布布扣,bubuko.com

时间: 2024-10-27 07:05:40

HDU 4067 Random Maze 费用流的相关文章

HDU 4067 Random Maze 费用流 构造欧拉通路

题目链接:点击打开链接 题意: 给定n个点m条有向边的图, 起点s ,终点t 下面m条边 u,v, a,b  若选择这条边花费为a, 不选择花费为b 构造一条欧拉通路使得起点是s,终点是t,且花费最小. 思路: 首先能想到一个简单的思路:假设所有边都选择,然后在费用流里跑,就会出现负环的问题.. 所以为了避免负环,让所有费用都为正值: int sum = 0; 若a>b, 则 费用要为正只能是 a-b, 默认这条边是删除是, sum += b, 那么如果我们要选择这条边,则从u=>v, 费用为

HDU 4067 Random Maze

题意: 一幅"随机图"定义为有如下性质的图: 有一个入口和一个出口 有向图 对于入口  出度比入度大1 对于出口  入度比出度大1 对于其他点  入度等于出度 现给出一幅有向图  每条边有2个决策--留下.扔掉  分别花费a和b  问  如果用最少的费用改造出"随机图" 思路: 网络流不错的题目  如果做过"混合图欧拉回路"(后文把这个问题成为p)那个zoj的题的话  这道题会有启发 来回忆p的做法  先将无向边随意定向  再利用度来将点划分成二

hdu 4067 Random Maze 最小费用最大流

题意: 给出n个点,m条边,入口s和出口t,对于每条边有两个值a,b,如果保留这条边需要花费:否则,移除这条边需要花费b. 题目要求用最小费用构造一个有向图满足以下条件: 1.只有一个入口和出口 2.所有路都是唯一方向 3.对于入口s,它的出度 = 它的入度 + 1 4.对于出口t,它的入度 = 它的出度 + 1 5.除了s和t外,其他点的入度 = 其出度 最后如果可以构造,输出最小费用:否则输出impossible. 思路: 表示建图太神..给个博客链接:http://www.cppblog.

POJ 2135 Farm Tour &amp;&amp; HDU 2686 Matrix &amp;&amp; HDU 3376 Matrix Again 费用流求来回最短路

累了就要写题解,最近总是被虐到没脾气. 来回最短路问题貌似也可以用DP来搞,不过拿费用流还是很方便的. 可以转化成求满流为2 的最小花费.一般做法为拆点,对于 i 拆为2*i 和 2*i+1,然后连一条流量为1(花费根据题意来定) 的边来控制每个点只能通过一次. 额外添加source和sink来控制满流为2. 代码都雷同,以HDU3376为例. #include <algorithm> #include <iostream> #include <cstring> #in

hdu 3315 My Brute 费用流,费用最小且代价最小

很常见的想法了= = #include <stdio.h> #include <iostream> #include <string.h> using namespace std; const int N=400; const int MAXE=200000; const int inf=1<<30; int head[N],s,t,cnt,n,m,ans; int d[N],pre[N]; bool vis[N]; int q[MAXE]; int V[N

HDU 3395 Special Fish(费用流)

题目地址:HDU 3395 刷了几道白书和CF上的非算法的智商题,感觉智商越来越接近负数了...还是先刷几道简单题缓缓.. 这题很简单,二分图模型,用费用流也可以,用KM也可以.不过需要注意的是这里是最大费用流,并不是最大费用最大流,区别在于是否是最大流,这题可以不是最大流,所以要当费用开始减少的时候停止继续流,来保证费用是最大的. 代码如下: #include <iostream> #include <cstdio> #include <string> #includ

HDU 5988 Coding Contest(费用流+浮点数)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5988 题目大意: 给定n个点,m条有向边,每个点是一个吃饭的地方,每个人一盒饭.每个点有S个人,有B盒饭.每条边只能被走c次,每条边上都有电线,第一个人通过的时候,不会破坏电线,从第二个人开始,每次都有概率p破坏掉电线.使得每个人都能吃饭,求最小破坏电线的概率. 解题思路: 题目要求我们求最小破坏电线的概率,就是一个最小乘积问题,加上log可以将其转变为加法,那样就可以使用费用刘来解决了. 按以下方

HDU 2686 Matrix(最大费用流)

Matrix Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1890    Accepted Submission(s): 1005 Problem Description Yifenfei very like play a number game in the n*n Matrix. A positive integer numbe

HDU 3315 My Brute(费用流)

题目地址:HDU 3315 这个题的思路完全是自己想出来的,自我感觉挺巧妙的...(大牛勿喷...)对大胆建图又多了一份信心. 具体思路是构造一个二分图,Si连源点,Xi连汇点,流量都是1,费用0.然后当Si可以赢Xj的时候,就对这两人连一条边,费用值为-Vi*1000,如果i==j的话,费用值就再减1,因为题目要求尽量不改变原先的顺序,所以说应该尽量让序号相同的对打.而费用值减1的话,会优先考虑序号相同的,而且让费用扩大了1000倍,此时也不会改变主要的分数因素大小.同理,输的话,费用值为Vi