【网络流】【SCOI2007】蜥蜴

Description
在一个r行c列的网格地图中有一些高度不同的石柱,一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外。 每行每列中相邻石柱的距离为1,蜥蜴的跳跃距离是d,即蜥蜴可以跳到平面距离不超过d的任何一个石柱上。石柱都不稳定,每次当蜥蜴跳跃时,所离开的石柱高度减1(如果仍然落在地图内部,则到达的石柱高度不变),如果该石柱原来高度为1,则蜥蜴离开后消失。以后其他蜥蜴不能落脚。任何时刻不能有两只蜥蜴在同一个石柱上。
Input
输入第一行为三个整数r,c,d,即地图的规模与最大跳跃距离。以下r行为石竹的初始状态,0表示没有石柱,1~3表示石柱的初始高度。以下r行为蜥蜴位置,“L”表示蜥蜴,“.”表示没有蜥蜴。
Output
输出仅一行,包含一个整数,即无法逃离的蜥蜴总数的最小值。
Sample Input
5 8 2
00000000
02000000
00321100
02000000
00000000
........
........
..LLLL..
........
........

Sample Output
1

HINT

100%的数据满足:1<=r, c<=20, 1<=d<=3
一道比較簡單的最大流算法。

拆點:將每個點拆成上下兩個點,若該位置有石柱,則將該位置從上點到下點連一條邊,權值為該石柱的高度;

若能從A石柱跳到B石柱,則從A位置的下點到B位置的上點連一條權值為正無窮的邊;

增加超級源S,從S到每個有蜥蜴的位置的上點連一條權值為1的邊;

增加超級汇T,從能夠跳出邊界的每一個石柱的下點到T連一條權值為正無窮的邊。


然後用SAP算法求解即可。

Accode:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <bitset>
#include <cmath>

const char fi[] = "lizard.in";
const char fo[] = "lizard.out";
const int maxN = 1010;

const int maxR = 30;
const int MAX = 0x3fffff00;
const int MIN = -MAX;

int h[maxR][maxR];
int f[maxN][maxN];
int d[maxN], cnt[maxN];
int x[maxN], y[maxN];
int up[maxR][maxR];
int down[maxR][maxR];
std::bitset <maxN> t;

int N, M, n = 1, D, ans;

  void init_file()
  {
    freopen(fi, "r", stdin);
    freopen(fo, "w", stdout);
  }

  inline int sqr(int x) {return x * x; }

  void readdata()
  {
    scanf("%d%d%d", &N, &M, &D);
    for (int i = 1; i < N + 1; ++i)
    {
      getchar();
      for (int j = 1; j < M + 1; ++j)
      {
        h[i][j] = getchar() - '0';
        if (h[i][j] > 0)
        {
          t.set(++n);
          up[i][j] = n;
          x[n] = i;
          y[n] = j;
          f[n][n + 1] = h[i][j];
    //注意上一行的n + 1不能用++n,
    //因為同一表達式的各部分計算順序不確定。
          t.reset(++n);
          down[i][j] = n;
          x[n] = i;
          y[n] = j;
        }
      }
    }
    ++n;
    for (int i = 1; i < N + 1; ++i)
     for (int j = 1; j < M + 1; ++j)
      if (h[i][j] > 0 && (i <= D ||
        j <= D || i > N - D || j > M - D))
        f[down[i][j]][n] = MAX;
    for (int i = 2; i < n; ++i)
     for (int j = 2; j < n; ++j)
      if (!t.test(i) && t.test(j)
        && (x[i] != x[j] || y[i] != y[j])
        && sqrt(sqr(x[i] - x[j])
        + sqr(y[i] - y[j])) <= D)
          f[i][j] = MAX;
    for (int i = 1; i < N + 1; ++i)
    {
      getchar();
      for (int j = 1; j < M + 1; ++j)
       if (getchar() == 'L' && h[i][j] > 0)
        {f[1][up[i][j]] = 1; ++ans; }
    }
  }

  int Sap(int u, int Lim)
  {
    if (u == n) return Lim;
    int tmp = 0;
    for (int v = 1; v < n + 1; ++v)
     if (f[u][v] > 0 && d[u] == d[v] + 1)
      {
        int k = Sap(v, std::min(Lim
          - tmp, f[u][v]));
        if (k <= 0) continue;
        f[u][v] -= k;
        f[v][u] += k;
        if ((tmp += k) == Lim) return tmp;
      }
    if (d[1] >= n) return tmp;
    if ((--cnt[d[u]]) <= 0) d[1] = n;
    ++cnt[++d[u]];
    return tmp;
  }

  void work()
  {
    cnt[0] = n;
    while (d[1] < n) ans -= Sap(1, MAX);
    printf("%d\n", ans);
  }

int main()
{
  init_file();
  readdata();
  work();
  exit(0);
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值