题解 P2472 【[SCOI2007]蜥蜴】

时间:2021-07-24 14:19:20

P2472 [SCOI2007]蜥蜴

题目背景

07四川省选

题目描述

在一个r行c列的网格地图中有一些高度不同的石柱,一些石柱上站着一些蜥蜴,你的任务是让尽量多的蜥蜴逃到边界外。

每行每列中相邻石柱的距离为1,蜥蜴的跳跃距离是d,即蜥蜴可以跳到平面距离不超过d的任何一个石柱上。石柱都不稳定,每次当蜥蜴跳跃时,所离开的石柱高度减1(如果仍然落在地图内部,则到达的石柱高度不变),如果该石柱原来高度为1,则蜥蜴离开后消失。以后其他蜥蜴不能落脚。任何时刻不能有两只蜥蜴在同一个石柱上。

输入输出格式

输入格式:

输入第一行为三个整数r,c,d,即地图的规模与最大跳跃距离。以下r行为石竹的初始状态,0表示没有石柱,1~3表示石柱的初始高度。以下r行为蜥蜴位置,“L”表示蜥蜴,“.”表示没有蜥蜴。

输出格式:

输出仅一行,包含一个整数,即无法逃离的蜥蜴总数的最小值。


解决这题的关键是理解这句话:石柱都不稳定,每次当蜥蜴跳跃时,所离开的石柱高度减1(如果仍然落在地图内部,则到达的石柱高度不变),如果该石柱原来高度为1,则蜥蜴离开后消失

我们把这句话转换一下,每个石柱 \(i\) 可以用石柱高度 \(hi\) 次。这是一个限制条件:使用次数,所以我们想到了最大流。

不能逃脱的蜥蜴数即为总蜥蜴数 - 逃脱的蜥蜴数,所以我们现在思考怎么建图

建模

最大流的核心在于限制

由限制条件,对网络流模型极其敏感我们可知道:石柱一定是要拆成两个点的,两点之间边容量为石柱高度 \(h\) ,这样可以限制一个石柱只能跳 \(h\) 次

由最大流答案不可能大于蜥蜴总数,所以我们可以联想到由汇点向蜥蜴所在的石柱连边,容量为 1

由石柱之间在规定距离内可以相互到达,*都会我们可以在距离内的石柱连一条容量为INF的边

由要跳出去可得:在跳到外围距离小于d的石柱连一条容量为INF的边

注意:因为每次都要消耗石柱高度,所以连入石柱的边连 石柱1 ,连出石柱的边从 石柱2 出发,从而达到路过石柱消耗高度这一限制

Code

#include<iostream>
#include<cstdio>
#include<queue>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
int RD(){
int out = 0,flag = 1;char c = getchar();
while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
return flag * out;
}
const int maxn = 100019,INF = 1e9;
int num,nume = 1,tot;
int lenx,leny,d;
int map[190][190];
int L[190][190];
int s,t,maxflow;
int head[maxn];
struct Node{
int v,dis,nxt;
}E[maxn << 2];
void add(int u,int v,int dis){
E[++nume].nxt = head[u];
E[nume].v = v;
E[nume].dis = dis;
head[u] = nume;
}
int lev[maxn];
bool bfs(){
queue<int>Q;
memset(lev,0,sizeof(lev));
Q.push(s);
lev[s] = 1;
while(!Q.empty()){
int u = Q.front();
Q.pop();
for(int i = head[u];i;i = E[i].nxt){
int v = E[i].v;
if(E[i].dis && !lev[v]){
lev[v] = lev[u] + 1;
Q.push(v);
if(v == t)return 1;
}
}
}
return 0;
}
int Dinic(int u,int flow){
if(u == t)return flow;
int rest = flow,k;
for(int i = head[u];i;i = E[i].nxt){
int v = E[i].v;
if(E[i].dis && lev[v] == lev[u] + 1 && rest){
k = Dinic(v,min(rest,E[i].dis));
if(!k)lev[v] = 0;
E[i].dis -= k;
E[i ^ 1].dis += k;
rest -= k;
} }
return flow - rest;
}
double getdis(int x1,int y1,int x2,int y2){
return ((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
int getindex(int x,int y){
return (x - 1) * leny + y;
}
int main(){
lenx = RD();leny = RD();d = RD();
char temp;
for(int i = 1;i <= lenx;i++){
for(int j = 1;j <= leny;j++){
cin>>temp;
map[i][j] = temp - '0';
}
}
for(int i = 1;i <= lenx;i++){
for(int j = 1;j <= leny;j++){
cin>>temp;
if(temp == 'L'){
L[i][j] = 1;
num++;
}
}
}
//石头1部从1到tot(lenx * leny),2部 + tot,石头编号为(i - 1) * leny + j
tot = lenx * leny;
s = tot * 2 + 1,t = s + 1;
for(int i = 1;i <= lenx;i++){
for(int j = 1;j <= leny;j++){
int I = getindex(i,j);
if(map[i][j]){
add(I,I + tot,map[i][j]);
add(I + tot,I,0);
if(i + d > lenx || i - d < 1 || j + d > leny || j - d < 1){
add(I + tot,t,INF);
add(t,I + tot,INF);
}
if(L[i][j]){
add(s,I,1);
add(I,s,0);
}
}
}
}
for(int x1 = 1;x1 <= lenx;x1++){
for(int y1 = 1;y1 <= leny;y1++){
for(int x2 = 1;x2 <= lenx;x2++){
for(int y2 = 1;y2 <= leny;y2++){
if(getdis(x1,y1,x2,y2) <= d * d){
int u = getindex(x1,y1),v = getindex(x2,y2);
add(u + tot,v,INF);
add(v,u + tot,0);
add(v + tot,u,INF);
add(u,v + tot,0);
}
}
}
}
}
int flow = 0;
while(bfs())while(flow = Dinic(s,INF))maxflow += flow;
printf("%d\n",num - maxflow);
return 0;
}