bzoj1066 (最大流)

1066: [SCOI2007]蜥蜴

Time Limit: 1 Sec   Memory Limit: 162 MB
Submit: 2497   Solved: 1233
[ Submit][ Status][ Discuss]

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


解题思路:

首先这道题可以看出是最大流,然后这道题特殊的是点上也有限制,所以对于每个点拆成两个,再建一条唯一的边,跑一边最大流。


#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
int n,m,d;
int len=1;
struct data
 {
  int to,v,next;
 }e[500001];
int h[1000];
int dis[1000];
int q[100000];
int zhi[30][30];
int mark[30][30];


void insert(int x,int y,int w){++len; e[len].to=y; e[len].v=w; e[len].next=h[x]; h[x]=len;}
bool bfs()
 {
  memset(dis,-1,sizeof(dis));
  int tail=1; int head=0; q[tail]=0; dis[0]=0;
  while (head<tail)
  {
  ++head;
  int u=h[q[head]];
  while (u!=-1)
  {
  if (dis[e[u].to]==-1 && e[u].v)
  {
  dis[e[u].to]=dis[q[head]]+1;
  ++tail; q[tail]=e[u].to;
}
u=e[u].next;
  }
 }
if (dis[801]==-1) return 0;else return 1;
 }


int dinic(int now,int sum)
 {
  if (now==801) return sum;
  int u=h[now]; int used=0;
  while (u!=-1)
  {
  if (dis[e[u].to]==dis[now]+1 && e[u].v)
  {
  int w=dinic(e[u].to,min(sum-used,e[u].v));
  used+=w;
  e[u].v-=w; e[u^1].v+=w;
  if (used==sum) return sum;
}
u=e[u].next;
 }
if (used==0) dis[now]=-1;
return used;
 }


int main()
{
scanf("%d %d %d",&n,&m,&d);
memset(h,-1,sizeof(h));
char c[21]; int tot=0;
for (int i=1;i<=n;++i)
 {
  scanf("%s",c);
  for (int j=1;j<=m;++j)
  {
  int g=int(c[j-1])-48;
  zhi[i][j]=g;
  ++tot; insert(tot,tot+1,g);
       ++tot; insert(tot,tot-1,0); mark[i][j]=tot-1;
}
 }
for (int i=1;i<=n;++i)
for (int j=1;j<=m;++j)
       for (int x1=1;x1<=n;++x1)
        for (int y1=1;y1<=m;++y1)
          if (i!=x1 || y1!=j)
           if (abs(i-x1)+abs(j-y1)<=d && zhi[i][j] && zhi[x1][y1])
            {
            insert(mark[i][j]+1,mark[x1][y1],zhi[i][j]); insert(mark[x1][y1],mark[i][j]+1,0);
}
int ass=0;
for (int i=1;i<=n;++i)
{
scanf("%s",c);
for (int j=1;j<=m;++j)
{
if (j<=d || i<=d || (m-j+1)<=d || (n-i+1)<=d && zhi[i][j])  {insert(mark[i][j]+1,801,0x7fffffff);insert(801,mark[i][j]+1,0);}
if (c[j-1]=='L')++ass;
if (c[j-1]=='L' && zhi[i][j])
{
insert(0,mark[i][j],1); insert(mark[i][j],0,0);
}
 }
}
    int ans=0;
    while (bfs())
     {
      ans+=dinic(0,0x7fffffff);
}
    cout<<ass-ans;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值