http://acm.sgu.ru/problem.php?contest=0&problem=176
题意:
给你N个点和一些这些点之间的边,每条边都有一个流量上界和流量下界,求1号结点
到N号结点的一个最小流,使得满足每条边的流量上下界的限制。
思路:
这是一道典型的有上下界的最小流的题目(求最大流是一样的算法),具体的方法介绍
请看这篇论文:《一种简易的方法求解流量有上下界的网络中网络流问题》。下面我就
大致地介绍一下这题的思路。首先很明显这是一个求有上下界流量的网络流的模型,我
们平时做得很多的都是只有一个流量上界的模型,这样我们就不能用原来的方法来处理
我们这里遇到的问题,在上面的论文中介绍了一种简易的求法,该算法的正确性是基于
下面的定理的:判断f是否是一个无源汇网络的可行流,可以通过判断该无源汇网络的改
进网络的源点的流出的流是否都是满流来实现,该改进网络的容量g[i] [j] = C[i][j] - B[i][j]
(具体请看论文)。这样我们就可以先二分答案,然后求出改进网络的最大流,判断是
否与源点流出的流相等,从而可以判断f是否是原无源汇的一个可行流。
代码:
#include<stdio.h>
#include<string.h>
#define CC(m,what) memset(m , what ,sizeof(m))
int N , M ;
const int inf = (1<<30) ;
const int MAXN = 110 ;
struct Node{
int a , b ;
}edge[MAXN*MAXN] ;
int C[MAXN][MAXN] ,B[MAXN][MAXN];
int g[MAXN][MAXN] ;
int dis[MAXN] , gap[MAXN] , pre[MAXN] , cur[MAXN] ;
void checkmin(int &a, int b){
a = a > b ? b : a ;
}
int sap(int s, int t, int nodenum){
CC(dis, 0) ; CC(gap, 0) ; CC(cur , 0) ;
int maxflow = 0 , u = pre[s] = s , aug = inf ;
gap[0] = nodenum ;
while( dis[s] < nodenum ){
loop :
for(int v=cur[u] ; v<nodenum ; v++)
if( g[u][v] && dis[u]==dis[v]+1){
checkmin( aug, g[u][v] ) ;
pre[v] = u ; u = cur[u] = v;
if(v == t){
maxflow += aug ;
for(u=pre[u] ; v!=s ; v=u,u=pre[u]){
g[u][v] -= aug ;
g[v][u] += aug ;
}
aug = inf ;
}
goto loop ;
}
int mindis = nodenum ;
for(int v = s ; v < nodenum ; v ++ ){
if( g[u][v] && mindis > dis[v] ){
cur[u] = v ;
mindis = dis[v] ;
}
}
if( (--gap[ dis[u] ]) == 0) break ;
gap[ dis[u] = mindis + 1 ]++ ;
u = pre[u] ;
}
return maxflow ;
}
bool is_ok(){
int sum = 0 ;
int s = 0 , t = N + 1;
for(int i=1;i<=N;i++){
int res = 0 ;
for(int j=1;j<=N;j++){
g[i][j] = C[i][j] - B[i][j] ;
res += B[j][i] - B[i][j] ;
}
if( res > 0 ){
g[s][i] = res ;
sum += res ;
}
else{
g[i][t] = -res ;
}
}
int ans = sap(s , t , N + 2);
if(ans == sum) return 1 ;
else return 0 ;
}
int main(){
int a, b, c, d ;
while(scanf("%d%d",&N,&M)==2){
CC(C,0) ; CC(B,0) ;
for(int i=1;i<=M;i++){
scanf("%d%d%d%d",&a,&b,&c,&d) ;
edge[i].a = a ; edge[i].b = b ;
C[a][b] = c ;
if(d) B[a][b] = c ;
else B[a][b] = 0 ;
}
int t1 = 0 ;
for(int i=2;i<=N;i++){
t1 += C[1][i] ;
}
t1++ ;
int low = 0 , high = t1 , mid ;
while(low < high){
B[N][1] = 0 ;
mid = (low + high) >> 1 ;
C[N][1] = mid ;
if( is_ok() ){
high = mid ;
}
else{
low = mid + 1 ;
}
}
if(low == t1){
printf("Impossible\n");
continue ;
}
printf("%d\n",low);
C[N][1] = low ;
is_ok() ;
for(int i=1;i<M;i++){
a = edge[i].a ;
b = edge[i].b ;
printf("%d ",C[a][b]-g[a][b]) ;
}
a = edge[M].a ; b = edge[M].b ;
printf("%d\n",C[a][b]-g[a][b]) ;
}
return 0 ;
}