bzoj 1066 [SCOI2007] 蜥蜴 题解

1066: [SCOI2007]蜥蜴

Time Limit: 1 Sec  Memory Limit: 162 MB
Submit: 1286  Solved: 620
[ Submit][ Status]

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

这是我的第一道非模板的网络流题目,值得纪念。

HHD大神的怂恿下,我尝试地做了这道题。结果不管怎么画图,还是不能把图建起来。

最大的一个难点在于,我发现它的权值在点上而非边上。(有点像我最近做的USACO training上的题目)

最后还是参考了HHD大牛的题解,即所谓的拆点法。

    对于每个石柱,我们把它拆成2个点,称为入点和出点,入点和出点连一条边,容量为高度,如果A石柱能跳到B石柱,就把出点A和入点B连一条边,容量为无限,这条边模拟的是跳的过程。如果该石柱能跳出去,就在该石柱出点和汇点T连一条边,容量无限。这条边模拟跳出去的过程。 如果该石柱有蜥蜴,在源点S和该石柱入点连一条边,容量为1.
这样的话,每个有蜥蜴的石柱上都有一只蜥蜴,如果该蜥蜴想从该石柱跳开,必定要先从该石柱入点跑到该石柱出点,这样会消耗1的容量,然后跳出去后,跳到另一石柱时,想再跳的话,又会消耗那个石柱的容量 ,直到跳到汇点T,贡献了1的流量。所以这幅图的最大流表示的就是能逃脱的蜥蜴数量,输出答案时减一下就行了。

为了加深理解,我尝试地画了一幅图。bzoj 1066 [SCOI2007] 蜥蜴 题解_第1张图片                      以下是代码:

#include<stdio.h>
#include<cstring>
#include<iostream>
const int INF=999999;
char c,u,a[21][21];
int map[1002][1002],fin[21][21],n,m,d,cnt,ant,ans,i,j,x[20001],pre[1001];
using namespace std;
void make_picture(int x,int y)
{
  int now=fin[x][y]+1;
  for (int i=1;i<=n;i++)
    for (int j=1;j<=m;j++)
      if ((i!=x||j!=y)&&(a[i][j]>'0'))
      {
        if (d*d>=(x-i)*(x-i)+(y-j)*(y-j))
          map[now][fin[i][j]]=INF;
      }
}
void go_down()
{
  for (int i=1;i<=n;i++)
    for (int j=1;j<=m;j++)
      if (i-d<1||i+d>n||j-d<1||j+d>m)
      {
        int now=fin[i][j]+1;
        map[now][cnt]=INF;
      }
}
void flow()
{
  while (true)
  {
    int h=0,t=1;x[1]=0;
    memset(pre,-1,sizeof(pre));
    while (h<t)
    {
      int now=x[++h];
      for (int i=1;i<=cnt;i++)
        if (pre[i]<0&&map[now][i]>0)
        {
          x[++t]=i;pre[i]=now;
        }
      if (pre[cnt]>0) break;
    }
    if (pre[cnt]<0) break;int small=INF;
    for (i=cnt;i!=0;i=pre[i]) small=min(map[pre[i]][i],small);
    for (i=cnt;i!=0;i=pre[i]) {map[pre[i]][i]-=small;map[i][pre[i]]+=small;}
    ans+=small;
  }
}
int main()
{
  scanf("%ld%ld%ld",&n,&m,&d);
  scanf("%c",&u);cnt=0;
  for (i=1;i<=n;i++)
  {
    for (j=1;j<=m;j++)
    {
      scanf("%c",&a[i][j]);
      if (a[i][j]!='0')
      {
        cnt++;fin[i][j]=cnt;
        map[cnt][cnt+1]=a[i][j]-48;cnt++;
      }
    }
    scanf("%c",&u);
  }
  for (i=1;i<=n;i++)
  {
    for (j=1;j<=m;j++)
    {
      scanf("%c",&c);
      if (c=='L') {ant++;map[0][fin[i][j]]=1;}
      if (a[i][j]>'0') {make_picture(i,j);}
    }
    scanf("%c",&u);
  }
  cnt++;go_down();
  flow();
  printf("%ld",ant-ans);
  return 0;
}




你可能感兴趣的:(题解,网络流,bzoj)