【GDOI2004】破坏行动 PASCAL

破坏行动(GDOI2004)

 

问题描述:

恐布组织伊阿德尔卡的首领拉本打算摧毁一个敌对势力的石油运输系统。这个石油运输系统可以看成一个运输网络,由许多的节点和连接节点的管道构成。只有地点A生产石油,而生产的石油则通过管道输送到地点B,石油不能在中间节点累积。管道是双向的,每条管道连接两个不同的节点,而每两个节点间最多只有一条管道相连。每条管道有一个抗压指数,当石油的流量超过这个数管道就会爆炸。A地生产石油的速度可以认为是非常快的,但由于管道抗压指数的原因,能运到B的有一个上限。拉本知道敌对势力采用了某种方案使得他们能输送最多的石油,但他不知道这个具体的方案是什么。拉本有一种特殊的物质,可以让一条管道的抗压指数下降1。作为伊阿德尔卡首席恐布程序员,你的任务就是告诉拉本,让哪些管道的抗压指数下降,一定可以使管道爆炸从而摧毁运输网络。

输入格式:

第一行包含四个整数n m s t ,表示有n个节点(编号为1,2,…,n), m 条管道,s t 分别是A地和B地的编号。2<=n<=130,0<=m<=n*(n-1)/2,1<=s,t<=n .

接下来m行每行描述一条管道,包含三个整数i j c。I 和j分别为管道连接的两个节点,c为这条管道的抗压指数。1<=I,j<=n, 1<=C<=10000。

输出格式:

第一行输出抗压指数减少1就必定爆炸的管道的条数K。

接下来K行每行输出一个整数P(1<=p<=m),说明第p条管道如果抗压指数减少1就必定爆炸,序号P接照管道输入的顺序,并按照P的升序输出。

输入输出样例:

destroy.in

4 4 1 4

2 4 100

1 2 1

3 1 100

4 3 1

destroy.out

2

2

4

 

 

问题是一个最大流问题,但不是只套用最大流算法.

因为所求的油管编号是“必爆”的,而最大流路径并不唯一。

 如输入

5 5 1 5

1 2 2

1 3 2

4 2

3 4 2

4 5 2

应输出

1

5

而不是

3

1

3

5

判断油管是否“必爆”的方法:

求出一条最大流,把最大流上相连的两点直接相连的弧删去,用FLOYD判断这两个点还是否连通。

如不连通则是必爆。

 

本图还是一个无向图。当一条弧容量是c,正方向流量为f时,反方向的容量为c+f,流量是-f

 

 

type
 net=record
 c,f:longint;
 end;
 node=record
 s,b:integer;
 end;

var
  n,s,t,x,a,nx:longint;
  dian:array[1..150]of node;
  xian,xiann:array[1..150,1..150]of net;
  mm:array[1..9000,1..2]of longint;

procedure init;
var
 i,j:longint;
begin
 read(n);
 read(nx);
 read(s,t);
 for i:=1 to nx do
  begin
   read(mm[i,1],mm[i,2],xian[mm[i,1],mm[i,2]].c);
   xian[mm[i,2],mm[i,1]].c:=xian[mm[i,1],mm[i,2]].c;
  end;
end;

function find:longint;
var
 i:longint;
begin
 for i:=1 to n do
  if (dian[i].s<>0)and(dian[i].b=0) then begin find:=i; exit; end;
 find:=0;
end;

function ford:boolean;
var
 i,j,m:longint;
begin
 m:=s-1;
 fillchar(dian,sizeof(dian),0);
 dian[s].s:=s;
 while m<>t do
  begin
   m:=find;
   if m=0 then exit(true);
    for i:=1 to n do
    if (dian[i].s=0)and((xian[m,i].c<>0)or(xian[i,m].c<>0)) then
    begin
     if (xian[m,i].f<xian[m,i].c)and(xian[m,i].f>=0) then dian[i].s:=m;
     if (xian[i,m].f>0) then dian[i].s:=-m;
    end;
   dian[m].b:=1;
  end;

 m:=t;
 a:=maxlongint;
 while m<>s do
  begin
  j:=m; m:=abs(dian[m].s);
  if dian[j].s>0 then x:=xian[m,j].c-xian[m,j].f;
  if dian[j].s<0 then x:=xian[j,m].c;
  if x<a then a:=x;
  end;
 ford:=false;
end;

procedure change;
var
 i,m,j:longint;
begin
 m:=t;
 while m<>s do
  begin
   j:=m; m:=abs(dian[m].s);
   if dian[j].s>0 then  begin inc(xian[m,j].f,a); dec(xian[j,m].f,a); end;
   if dian[j].s<0 then  begin dec(xian[j,m].f,a); inc(xian[m,j].f,a); end;
  end;
end;

procedure print;
var
 i,tot:longint;
begin
 tot:=0;
 for i:=1 to nx do
 if (xian[mm[i,1],mm[i,2]].f=xian[mm[i,1],mm[i,2]].c)
 or (xian[mm[i,2],mm[i,1]].f=xian[mm[i,2],mm[i,1]].c) then inc(tot);
 writeln(tot);
 for i:=1 to nx do
 if (xian[mm[i,1],mm[i,2]].f=xian[mm[i,1],mm[i,2]].c)
 or (xian[mm[i,2],mm[i,1]].f=xian[mm[i,2],mm[i,1]].c) then writeln(i);
end;

procedure floyd;
var
 i,j,k:longint;
begin
 xiann:=xian;

 for i:=1 to n do
  for j:=1 to n do
    xian[i,j].c:=xian[i,j].c-xian[i,j].f;

 for k:=1 to n do
  for i:=1 to n do
   for j:=1 to n do
   if (xian[i,k].c<>0)and(xian[k,j].c<>0) then
   begin
   if (xian[i,j].f<>0) then
   xiann[i,j].f:=-1;
   xian[i,j].c:=1;
   end;
 xian:=xiann;
end;

procedure work;
begin
 while not ford do change;
 floyd;
 print;
end;

begin
 init;
 work;
end.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值