ZJOI 2009 狼和羊的故事 网络流

题目:

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

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

思路:

一开始没往网络流想,想了一些乱七八糟的找规律方法,无果。看了标签才想到做法......

将每个格子当做一个点,源点S向所有1连边,所有2向汇点T连边,其边权均为INF;然后0或1向相邻的0或2的点连边,其边权均为1;这样问题转化为使源点不能到达汇点的最小割。权当复习一遍Dinic吧。。。

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <queue>
#include <vector>
#include <cstring>

#define For(i,j,k) for(int i = j;i <= (int)k;i++)
#define Forr(i,j,k) for(int i = j;i >= (int)k;i--)
#define Set(i,j) memset(i, j, sizeof(i)) 
#define pb push_back

using namespace std;

const int N = 110, M = N * N;
const int d[4][2] = {0, 1, 0, -1, 1, 0, -1, 0};

int n, m, cnt = 1, Map[N][N], id[N][N];

struct Edge{
	int to, flow, cap;
};

struct Dinic{
	vector<int> G[M];
	int S, T, e;
	int dis[M], cur[M];
	bool vis[M];
	Edge E[M<<4];

	void Add(int x, int y, int f){
		E[e] = (Edge){y, 0, f}, G[x].pb(e++);
		E[e] = (Edge){x, 0, 0}, G[y].pb(e++);
	}

	bool BFS(){
		Set(vis, 0);
		queue<int> q;
		q.push(S);
		vis[S] = 1, dis[S] = 0;
		while(!q.empty()){
			int h = q.front(); q.pop();
			For(i,0,G[h].size() - 1){
				Edge& s = E[G[h][i]];
				if(vis[s.to] || s.flow == s.cap) continue;
				vis[s.to] = 1, dis[s.to] = dis[h] + 1;
				q.push(s.to);
			}
		}
		return vis[T];
	}

	int DFS(int h, int f){
		if(h == T) return f;
		int sum = 0;
		for(int &i = cur[h];i < G[h].size() && sum < f;i++){
			Edge& s = E[G[h][i]];
			int t;
			if(dis[h] + 1 == dis[s.to] && (t = DFS(s.to, min(s.cap - s.flow, f - sum)))){
				sum += t;
				s.flow += t;
				E[G[h][i] ^ 1].flow -= t;
			}
		}
		return sum;
	}

	int Maxflow(int s, int t){
		S = s, T = t;
		int Ans = 0;
		while(BFS()){
			Set(cur, 0);
			Ans += DFS(S, 1e9);
		}
		return Ans;
	}

}S;

int main(){
	scanf("%d%d", &n, &m);
	For(i,1,n) For(j,1,m) scanf("%d", &Map[i][j]), id[i][j] = ++cnt;
	For(i,1,n) For(j,1,m){
		if(Map[i][j] == 1) S.Add(0, id[i][j], 1e9);
		else if(Map[i][j] == 2){
			S.Add(id[i][j], 1, 1e9);
			continue;
		}
		For(v,0,3){
			int x = i + d[v][0], y = j + d[v][1];
			if(Map[x][y] != 1) S.Add(id[i][j], id[x][y], 1);
		}
	}
	printf("%d\n", S.Maxflow(0, 1));
	return 0;
}


评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值