图论——容量有上下界的网络流

pku2396:

这个题是个典型的容量有上下界的网络流问题。

假设您已经对最大流的原理很了解,那么请继续看:

关于这种问题的解法,请看这篇论文,讲的很好。

http://wenku.baidu.com/view/0f3b691c59eef8c75fbfb35c.html

如果这篇论文看得没有问题了,那么我们来谈一下建图。

显然所有行的和与列的和是相同的,这是什么——网络流的流量平衡。

但是行和列中每个元素怎样限定呢?一种做法是:将每行、每列及每个元素看成一个点,然后把每个点拆成两个点,中间限制流量。。。。但是这样做点数有2000多个。。。。有点太大了~~   第二种做法:只将每一行和每一列看成一个节点,中间连边代表元素,然后给边加上上下界。看看是否存在可行流,如果存在就输出一个,否则impossible。

注意:

如果建图过程中一条边的上界<下界——无解

如果每列总和与每行总和不相等——循环流不守恒——无解

f(s,i)<c(s,i)或者f(i,t)<c(i,t)——新网络没有满流,也就是源网络存在某些边无法满足下界约束或者上界约束——无解

此题主要过程在于建图,建好了图直接dinic或者sap都行~注意是多组数据啊,每次开始前记得初始化,我debug了好一会儿才发现res数组忘记初始化了,泪奔~~。

代码比一般题有一点点多,打了40+分钟才打完,但也不是特别多,加上debug的过程才260+

测试数据地址:http://www.ida.liu.se/projects/progcontest/progsm/2003/

代码:

View Code
#include <iostream>
#include
<cstdio>
#include
<cstring>
usingnamespace std;

constint N =250;
constint inf =1000000;

int n, m, s, t, sumr[N], sumc[23], queue[N], lab[N], res[N][N], up[N][N], low[N][N], flow[N][N];

void initData()
{
int i, j, x, y, z, q;
char c;
memset(low,
0, sizeof(low));
memset(up,
0, sizeof(up));
scanf(
"%d%d", &n, &m);
for(i =1; i <= n; i++)
{
scanf(
"%d", sumr+i);
up[
0][i] = low[0][i] = sumr[i];
}
for(i =1; i <= m; i++)
{
scanf(
"%d", sumc+i);
up[i
+n][n+m+1] = low[i+n][n+m+1] = sumc[i];
}
for(i =1; i <= n; i++)
for(j =1; j <= m; j++)
up[i][j
+n] = inf;
scanf(
"%d", &q);
while(q--)
{
scanf(
"%d%d %c%d", &x, &y, &c, &z);
switch(c)
{
case'>':
{
z
++;
if(x==0&& y!=0)
{
for(i =1; i <= n; i++)
low[i][y
+n] = max(low[i][y+n], z);
break;
}
if(x!=0&& y==0)
{
for(j =1; j <= m; j++)
low[x][j
+n] = max(low[x][j+n], z);
break;
}
if(x==0&& y==0)
{
for(i =1; i <= n; i++)
for(j =1; j <= m; j++)
low[i][j
+n] = max(low[i][j+n], z);
break;
}
if(x!=0&& y!=0)
{
low[x][y
+n] = max(low[x][y+n], z);
break;
}
}
case'<':
{
z
--;
if(x==0&& y!=0)
{
for(i =1; i <= n; i++)
up[i][y
+n] = min(up[i][y+n], z);
break;
}
if(x!=0&& y==0)
{
for(j =1; j <= m; j++)
up[x][j
+n] = min(up[x][j+n], z);
break;
}
if(x==0&& y==0)
{
for(i =1; i <= n; i++)
for(j =1; j <= m; j++)
up[i][j
+n] = min(up[i][j+n], z);
break;
}
if(x!=0&& y!=0)
{
up[x][y
+n] = min(up[x][y+n], z);
break;
}
}
case'=':
{
if(x==0&& y!=0)
{
for(i =1; i <= n; i++)
{
up[i][y
+n] = min(up[i][y+n], z);
low[i][y
+n] = max(low[i][y+n], z);
}
break;
}
if(x!=0&& y==0)
{
for(j =1; j <= m; j++)
{
up[x][j
+n] = min(up[x][j+n], z);
low[x][j
+n] = max(low[x][j+n], z);
}
break;
}
if(x==0&& y==0)
{
for(i =1; i <= n; i++)
for(j =1; j <= m; j++)
{
up[i][j
+n] = min(up[i][j+n], z);
low[i][j
+n] = max(low[i][j+n], z);
}
break;
}
if(x!=0&& y!=0)
{
up[x][y
+n] = min(up[x][y+n], z);
low[x][y
+n] = max(low[x][y+n], z);
break;
}
break;
}
}
}
}

bool check()
{
int i, j, t1, t2, tot = n+m+1;
t1
= t2 =0;
for(i =1; i <= n; i++) t1 += sumr[i];
for(j =1; j <= m; j++) t2 += sumc[j];
if(t1 != t2) returnfalse;
up[tot][
0] = low[tot][0] = t1;
for(i =1; i <= n; i++)
for(j =1; j <= m; j++)
if(low[i][j+n] > up[i][j+n])
returnfalse;
returntrue;
}

void buildGraph()
{
int i, j, tmp;
s
= n+m+2;
t
= n+m+3;
memset(res,
0, sizeof(res));
for(i =0; i < s; i++)
{
tmp
=0;
for(j =0; j < s; j++) tmp += low[j][i];
for(j =0; j < s; j++) tmp -= low[i][j];
if(tmp >0) res[s][i] = tmp;
elseif(tmp <0)
res[i][t]
=-tmp;
}
for(i =0; i < s; i++)
for(j =0; j < s; j++)
res[i][j]
= up[i][j] - low[i][j];
memset(flow,
0, sizeof(flow));
}

bool bfs(int s, int t)
{
int head, tail, i, u;
memset(lab,
-1, sizeof(lab));
head
= tail =0;
queue[
0] = s;
lab[s]
=0;
while(head <= tail)
{
u
= queue[head++];
for(i =0; i <= t; i++)
if(res[u][i]>0&& lab[i]==-1)
{
lab[i]
= lab[u] +1;
queue[
++tail] = i;
}
if(lab[t] !=-1) returntrue;
}
returnfalse;
}

int dinicDfs(int delta, int u)
{
int i, sum =0, tmp;
if(u == t) return delta;
for(i =0; i <= t; i++)
if(res[u][i]>0&& lab[i]==lab[u]+1)
{
tmp
= dinicDfs(min(delta, res[u][i]), i);
sum
+= tmp;
delta
-= tmp;
res[u][i]
-= tmp;
res[i][u]
+= tmp;
flow[u][i]
+= tmp;
flow[i][u]
-= tmp;
}
return sum;
}

void maxFlow(int s, int t)
{
int tmp;
while(bfs(s, t))
tmp
= dinicDfs(inf, s);
}

void deal()
{
bool flag =true;
int i, j;
for(i =0; i < s; i++)
if(!(res[s][i]==0&& res[i][t]==0))
{
flag
=false;
break;
}
if(!flag) printf("IMPOSSIBLE\n");
else
{
for(i =1; i <= n; i++)
{
for(j =1; j < m; j++) printf("%d ", flow[i][j+n]+low[i][j+n]);
printf(
"%d\n", flow[i][m+n]+low[i][m+n]);
}
}
}

void debug()
{
int i, j;
for(i =1; i <= n; i++)
for(j =1; j <= m; j++)
printf(
"%d -> %d : %d %d\n", i, j, low[i][j+n], up[i][j+n]);
}

int main()
{
int Case, ct =0;
freopen(
"data.txt", "r", stdin);
freopen(
"ans.txt", "w", stdout);
scanf(
"%d", &Case);
while(Case--)
{
if(ct) printf("\n");
initData();
// debug();
if(check())
{
buildGraph();
maxFlow(s, t);
deal();
}
else printf("IMPOSSIBLE\n");
ct
++;
}
return0;
}

  

转载于:https://www.cnblogs.com/zxndgv/archive/2011/08/09/2132254.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值