1. 程式人生 > 實用技巧 >[SCOI2007]蜥蜴 (網路流)

[SCOI2007]蜥蜴 (網路流)

剛學習網路流
一上午熟悉\(Dinic\)板子+就調過了這一個題

題目描述

在一個r行c列的網格地圖中有一些高度不同的石柱,一些石柱上站著一些蜥蜴,你的任務是讓儘量多的蜥蜴逃到邊界外。 每行每列中相鄰石柱的距離為1,蜥蜴的跳躍距離是d,即蜥蜴可以跳到平面距離不超過d的任何一個石柱上。石柱都不穩定,每次當蜥蜴跳躍時,所離開的石柱高度減1(如果仍然落在地圖內部,則到達的石柱高度不變),如果該石柱原來高度為1,則蜥蜴離開後消失。以後其他蜥蜴不能落腳。任何時刻不能有兩隻蜥蜴在同一個石柱上。

輸入格式

輸入第一行為三個整數r,c,d,即地圖的規模與最大跳躍距離。以下r行為石竹的初始狀態,0表示沒有石柱,1~3表示石柱的初始高度。以下r行為蜥蜴位置,“L”表示蜥蜴,“.”表示沒有蜥蜴。

輸出格式

輸出僅一行,包含一個整數,即無法逃離的蜥蜴總數的最小值。

樣例

樣例輸入

5 8 2
00000000
02000000
00321100
02000000
00000000
........
........
..LLLL..
........
........

樣例輸出

1

資料範圍與提示

100%的資料滿足:1<=r, c<=20, 1<=d<=4

Solution

看上去就是每個點有限制能跳過幾只蜥蜴
就相當於邊權中的流量上限
考慮如何建圖
由於對於單個點無法增加邊權
選擇拆點成邊
拆出的點之間的邊權就是流量的限制
首先建立超級源點\(s=0\)
因為圖中有\(n*m\)個點
拆完之後\(2*n*m\)個點
所以超級匯點是\(t=2*n*m+1\)


圖中建邊有四種

  • 由源點\(s\)出發,向所有蜥蜴所在的點建立邊權為\(1\)的邊
  • 所有有高度(就是蜥蜴可以跳躍的點)拆點,向\((i,j)\)對應的點\((i,j) + n*m\)建邊,邊權為當前點的高度,即流量限制
  • 由所有一次能夠跳出矩陣的點向超級匯點\(t\)建邊,邊權無限制,為\(inf\)
  • 矩陣內部互相能夠到達的點,邊權無限制,為\(inf\)

最後直接跑\(Dinic\)就行了
注意\(Dinic\)跑出來的是能夠逃離的蜥蜴的數量
要用總數量減一下

Code

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <queue>
#include <cmath>
#define min(a, b) ({register int AA = a, BB = b; AA < BB ? AA : BB;})
#define inf 10000009
using namespace std;

inline int read(){
	int x = 0, w = 1;
	char ch = getchar();
	for(; ch > '9' || ch < '0'; ch = getchar()) if(ch == '-') w = -1;
	for(; ch >= '0' && ch <= '9'; ch = getchar()) x = x * 10 + ch - '0';
	return x * w;
}

const int ss = 10010;

struct node{
	int to, nxt, w;
}edge[ss * 20];

int head[ss], tot = 1;
inline void add(register int u, register int v, register int w){
	edge[++tot].to = v;
	edge[tot].nxt = head[u];
	edge[tot].w = w;
	head[u] = tot;
}

int dis[ss], cur[ss];
int r, c, d, s, t;
bool vis[ss];
queue<int> q;
inline bool spfa(register int s){
	for(register int i = 0; i <= t; i++)
		vis[i] = 0, dis[i] = 0x3f3f3f3f, cur[i] = head[i];
	dis[s] = 0;
	q.push(s);
	while(!q.empty()){
		register int u = q.front();
		q.pop();
		vis[u] = 0;
		for(register int i = head[u]; i; i = edge[i].nxt){
			register int v = edge[i].to;
			if(dis[v] > dis[u] + 1 && edge[i].w){
				dis[v] = dis[u] + 1;
				if(!vis[v]) q.push(v), vis[v] = 1;
			}
		}
	}
	return dis[t] != 0x3f3f3f3f;
}

inline int dfs(register int u, register int flow){
	register int res = 0;
	if(u == t) return flow;
	for(register int i = cur[u]; i; i = edge[i].nxt){
		cur[u] = i;
		register int v = edge[i].to;
		if(dis[v] == dis[u] + 1 && edge[i].w){
			if(res = dfs(v, min(flow, edge[i].w))){
				edge[i].w -= res;
				edge[i ^ 1].w += res;
				return res;
			}
		}
	}
	return 0;
}

long long ans, cnt;
inline long long dinic(){
	register long long minflow = 0;
	while(spfa(s)){
		while(minflow = dfs(s, 0x7fffffff))
			ans += minflow;
	}
	return ans;
}

inline double getdis(register int a, register int b, register int c, register int d){
	return sqrt((a - c) * (a - c) + (b - d) * (b - d));
}

inline int change(register int i, register int j){
	return (i - 1) * c + j;
}

int a[25][25], flag[25][25];
char ch[25];
signed main(){
	r = read(), c = read(), d = read();
	for(register int i = 1; i <= r; i++){
		scanf("%s", ch + 1);
		for(register int j = 1; j <= c; j++)
			a[i][j] = ch[j] - '0';
	}
	for(register int i = 1; i <= r; i++){
		scanf("%s", ch + 1);
		for(register int j = 1; j <= c; j++)
			if(ch[j] == 'L') flag[i][j] = 1;
	}
	
	s = 0, t = 2 * c * r + 1;
	for(register int i = 1; i <= r; i++)
		for(register int j = 1; j <= c; j++)
			if(flag[i][j] == 1){
				cnt++;
				add(s, change(i, j), 1);
				add(change(i, j), s, 0);
			}
	for(register int i = 1; i <= r; i++)
		for(register int j = 1; j <= c; j++)
			if(a[i][j]){
				add(change(i, j), change(i, j) + r * c, a[i][j]);
				add(change(i, j) + r * c, change(i, j), 0);
			}
	for(register int i = 1; i <= r; i++)
		for(register int j = 1; j <= c; j++){
			if(i > d && i <= r - d && j > d && j <= c - d) continue;
			if(a[i][j]){
				add(change(i, j) + r * c, t, inf);
				add(t, change(i, j) + r * c, 0);
			}
		}
	for(register int i = 1; i <= r; i++){
		for(register int j = 1; j <= c; j++){
			for(register int p = 1; p <= r; p++){
				for(register int q = 1; q <= c; q++){
					if(i == p && j == q) continue;
					if(getdis(i, j, p, q) <= (double)d && a[i][j] && a[p][q]){
						add(change(i, j) + r * c, change(p, q), inf);
						add(change(p, q), change(i, j) + r * c, 0);
					}
				}
			}
		}
	}
	printf("%lld\n", cnt - dinic());
	return 0;
}