题解 P2598 【[ZJOI2009]狼和羊的故事】

P2598 [ZJOI2009]狼和羊的故事

题目描述
“狼爱上羊啊爱的疯狂,谁让他们真爱了一场;狼爱上羊啊并不荒唐,他们说有爱就有方向......” Orez听到这首歌,心想:狼和羊如此和谐,为什么不尝试羊狼合养呢?说干就干! Orez的羊狼圈可以看作一个n*m个矩阵格子,这个矩阵的边缘已经装上了篱笆。可是Drake很快发现狼再怎么也是狼,它们总是对羊垂涎三尺,那首歌只不过是一个动人的传说而已。所以Orez决定在羊狼圈中再加入一些篱笆,还是要将羊狼分开来养。 通过仔细观察,Orez发现狼和羊都有属于自己领地,若狼和羊们不能呆在自己的领地,那它们就会变得非常暴躁,不利于他们的成长。 Orez想要添加篱笆的尽可能的短。当然这个篱笆首先得保证不能改变狼羊的所属领地,再就是篱笆必须修筑完整,也就是说必须修建在单位格子的边界上并且不能只修建一部分。

输入输出格式
输入格式:
文件的第一行包含两个整数n和m。接下来n行每行m个整数,1表示该格子属于狼的领地,2表示属于羊的领地,0表示该格子不是任何一只动物的领地。

输出格式:
文件中仅包含一个整数ans,代表篱笆的最短长度。


分析

要保证篱笆长最小而把狼和羊分开来,我们可以联想到最小割模型。一个图的最小割就是把图分为两个部分(源点及汇点不在同一部)的边权和。最小割可以用最大流算法求得。

建模

要说到网络流,重点就在于建模了,我们怎么把此网格图转换为最大流网络流呢?其实对于一个格子,我们可以把它看做与上下左右四个方向都有一条连边,而把这个格子抽象成一个点,如下图:

依据题意和最大流的经验,我们可以连边了:(我以羊的一部作为源点,所以)源点连羊,狼连汇点,若相邻的点事狼,则连一条容量为1的边(他的模型意义是:把羊和狼分开【割】需要消耗“1”)

但是对于0怎么办呢?这是本题的难点

可以思索一下,若是把0全部归为狼或者羊吧,感觉又会有更优解(事实也是这样,因为狼和羊是等价的【把狼从羊中隔离开来等价于把羊从狼中隔离开来】,所以这样单方面划分是肯定不正确的),那么怎么办呢

你可能不会,但你的最大流算法一定知道怎么做

我们这样连:源点---羊--(边A,c=1)--0--(边B, c=1)--狼---汇点

试想一下,你的篱笆的作用是分割狼和羊,0这些格子要么被划分到狼的领地,要么被划分到羊的领地,若是划分到狼那边,你的算法会割开靠近羊的那条边 A ,要是划分到羊这边,他会自动割开靠近狼的边 B 。一定不存在一种割的方式,使 A 和 B 同时被割开,因为你的算法知道,割一条就足以分开两点,不需要割第二条

所以,放手给程序去跑吧

# Code
```cpp

include

include

include

include

include

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 nume = 1;
int lenx,leny;
int map[190][190];
int mx[4] = {1,-1,0,0};
int my[4] = {0,0,1,-1};
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

    }
return flow - rest;
}

int getindex(int x,int y){
return (x - 1) * leny + y;
}
bool judge(int x,int y){
if(x < 1 || x > lenx || y < 1 || y > leny)return 0;
return 1;
}
/*for(int i = 1;i <= lenx;i++){
for(int j = 1;j <= leny;j++){

        }
    }*/

void build(){
for(int i = 1;i <= lenx;i++){
for(int j = 1;j <= leny;j++){
if(map[i][j] == 2){
add(s,getindex(i,j),INF);
add(getindex(i,j),s,0);
}
else if(map[i][j] == 1){
add(getindex(i,j),t,INF);
add(t,getindex(i,j),0);
}
}
}
for(int i = 1;i <= lenx;i++){
for(int j = 1;j <= leny;j++){
if(map[i][j] == 2 || map[i][j] == 0){
for(int k = 0;k < 4;k++){
int nx = i + mx[k],ny = j + my[k];
if(!judge(nx,ny))continue;
if(map[nx][ny] == 1 || map[nx][ny] == 0){
add(getindex(i,j),getindex(nx,ny),1);
add(getindex(nx,ny),getindex(i,j),0);
}
}
}
}
}
}
int main(){
lenx = RD();leny = RD();
for(int i = 1;i <= lenx;i++){
for(int j = 1;j <= leny;j++){
map[i][j] = RD();
}
}
s = lenx * leny + 1,t = s + 1;
build();
int flow = 0;
while(bfs())while(flow = Dinic(s,INF))maxflow += flow;
printf("%d\n",maxflow);
return 0;
}
```

猜你喜欢

转载自www.cnblogs.com/Tony-Double-Sky/p/9285535.html