【BZOJ1001】狼抓兔子

现在小朋友们最喜欢的"喜羊羊与灰太狼",话说灰太狼抓羊不到,但抓兔子还是比较在行的,而且现在的兔子还比较笨,它们只有两个窝,现在你做为狼王,面对下面这样一个网格的地形:

 

左上角点为(1,1),右下角点为(N,M)(上图中N=4,M=5).有以下三种类型的道路 1:(x,y)<==>(x+1,y) 2:(x,y)<==>(x,y+1) 3:(x,y)<==>(x+1,y+1) 道路上的权值表示这条路上最多能够通过的兔子数,道路是无向的. 左上角和右下角为兔子的两个窝,开始时所有的兔子都聚集在左上角(1,1)的窝里,现在它们要跑到右下解(N,M)的窝中去,狼王开始伏击这些兔子.当然为了保险起见,如果一条道路上最多通过的兔子数为K,狼王需要安排同样数量的K只狼,才能完全封锁这条道路,你需要帮助狼王安排一个伏击方案,使得在将兔子一网打尽的前提下,参与的狼的数量要最小。因为狼还要去找喜羊羊麻烦.



【题解】

乍一看是最大流问题,然后就发现直接写应该会爆,于是上网找了一下发现可以构造对偶图(也就是先连首尾,然后一个小回路当一个点,两个点之间连一条权值是原来两个回路中间边的权值的边,最后删掉连接首尾的边)然后就是求解最短路问题了。



【代码】

program p1001;

type
  edge = record
    y, next, l: longint;
  end;
var
  a: array[-1..6000000] of edge;
  b: array[0..1000, 0..1000, 0..1] of longint;
  pd: array[0..2000000] of boolean;
  d0, q, f: array[0..2000000] of int64;
  i, j, m, n, x, n0, k: longint;

  procedure insert(x, y, l: longint);
  begin
    Inc(a[-1].y);
    a[a[-1].y].y := y;
    a[a[-1].y].l := l;
    a[a[-1].y].next := f[x];
    f[x] := a[-1].y;
  end;

  procedure SPFA;
  var
    h, t, i, j, m: longint;
  begin
    for i := 0 to 2000000 do
      d0[i] := maxlongint div 2;
    q[0] := 0;
    h := 0;
    t := 0;
    pd[0] := True;
    d0[0] := 0;
    while h <> t + 1 do
    begin
      m := q[h];
      h := (h + 1) mod 5000000;
      pd[m] := False;
      i := f[m];
      while i <> 0 do
      begin
        j := a[i].y;
        if d0[m] + a[i].l < d0[j] then
        begin
          d0[j] := d0[m] + a[i].l;
          if not pd[j] then
          begin
            if d0[j] < d0[q[h]] then
            begin
              h := (h + 4999999) mod 5000000;
              q[h] := j;
            end
            else
            begin
              t := (t + 1) mod 5000000;
              q[t] := j;
            end;
            pd[j] := True;
          end;
        end;
        i := a[i].next;
      end;
    end;
  end;

begin
  readln(m, n);
  for i := 1 to m - 1 do
    for j := 1 to n - 1 do
    begin
      Inc(n0);
      b[i, j, 0] := n0;
      Inc(n0);
      b[i, j, 1] := n0;
    end;
  k := n0 + 1;
  for i := 1 to m do
    for j := 1 to n - 1 do
    begin
      Read(x);
      if i = 1 then
        insert(b[i, j, 1], k, x)
      else
      if i = m then
        insert(0, b[i - 1, j, 0], x)
      else
      begin
        insert(b[i, j, 1], b[i - 1, j, 0], x);
        insert(b[i - 1, j, 0], b[i, j, 1], x);
      end;
    end;
  for i := 1 to m - 1 do
    for j := 1 to n do
    begin
      Read(x);
      if j = 1 then
        insert(0, b[i, j, 0], x)
      else
      if j = n then
        insert(b[i, j - 1, 1], k, x)
      else
      begin
        insert(b[i, j, 0], b[i, j - 1, 1], x);
        insert(b[i, j - 1, 1], b[i, j, 0], x);
      end;
    end;
  for i := 1 to m - 1 do
    for j := 1 to n - 1 do
    begin
      Read(x);
      insert(b[i, j, 0], b[i, j, 1], x);
      insert(b[i, j, 1], b[i, j, 0], x);
    end;
  spfa;
  writeln(d0[k]);
end.


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值