Problem F: Landscaping
[Time Limit: 1 s quad Memory Limit: 256 MiB
]
题意
题意是给出一个(n*m)的格子,其中一些是低海拔的,一些是高海拔的,然后有(n+m)辆推土车从左到右,从上到下推过去,推土车在不同高度的块之前走需要花费(A)元,也可以花费(B)元改变一个块的海拔
思路
这个网络流真的好骚啊...
先说建图怎么建图,建图分成三步
- 从源点到所有低海拔的块建一条(B)流量的边
- 从所有高海拔的块到汇点建一条(B)流量的边
- 对于所有块,想四个方向建一条(A)流量的边
考虑其中的一条流量。
源点(frac{B}{——>})低海拔(frac{A}{——>})高海拔(frac{B}{——>})汇点
如果(A>B),那么能跑出的最大流是(B),也就是花费(B)元把低海拔变为高海拔或者把高海拔变为低海拔,如果(B>A),那么跑出的最大流是(A),也就是花费(A)元从低海拔走到高海拔。
我们发现,这个最大流,一定是这三条边中的最小值。思考他们的意义,如果选择第一条边或第三条边,也就是把低海拔变为高海拔或者高海拔变为低海拔,那么这时候网络流中(A)被忽略了,也符合实际意义,已经改变了块的海拔,不用花费(A)元在不同块之间走。如果选择第二条边,也就是从低海拔走到高海拔,这时候(B)被忽略,也符合实际意义不修改这个块的海拔,而是直接走。
那么为什么一个块要对四个方向建边而不是向四个方向上存在海拔差的块建边呢?因为网络流跑的过程中,旁边的块可能发生了海拔变化,我们无法知道哪些发生了变化,所以都要建一个(A)的边。
所以在网络流跑的时候,由于限制条件的存在,他这样建边跑出来的最大流就是答案。
#include <map>
#include <set>
#include <list>
#include <ctime>
#include <cmath>
#include <stack>
#include <queue>
#include <cfloat>
#include <string>
#include <vector>
#include <cstdio>
#include <bitset>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>
#define lowbit(x) x & (-x)
#define mes(a, b) memset(a, b, sizeof a)
#define fi first
#define se second
#define pii pair<int, int>
#define INOPEN freopen("in.txt", "r", stdin)
#define OUTOPEN freopen("out.txt", "w", stdout)
typedef unsigned long long int ull;
typedef long long int ll;
const int maxn = 1e5 + 10;
const int maxm = 1e5 + 10;
const ll mod = 1e9 + 7;
const ll INF = 1e18 + 100;
const int inf = 0x3f3f3f3f;
const double pi = acos(-1.0);
const double eps = 1e-8;
using namespace std;
int n, m, A, B;
int cas, tol, T;
struct Node{
int v, w;
int next;
} node[maxn];
int dis[4][2] = {1, 0, -1, 0, 0, 1, 0, -1};
char s[100][100];
int head[maxn];
int dep[maxn];
int getid(int x, int y) {
return (x-1)*m+y;
}
void init() {
tol = 0;
mes(head, -1);
}
void addnode(int u, int v, int w) {
node[tol].v = v;
node[tol].w = w;
node[tol].next = head[u];
head[u] = tol++;
}
bool bfs(int src, int des) {
mes(dep, -1);
dep[src] = 0;
queue<int> q;
while(!q.empty()) q.pop();
q.push(src);
while(!q.empty()) {
int u = q.front();
q.pop();
if(u == des) return true;
for(int i=head[u]; ~i; i=node[i].next) {
int v = node[i].v;
if(dep[v] == -1 && node[i].w) {
dep[v] = dep[u]+1;
q.push(v);
}
}
}
return false;
}
int dfs(int src, int des, int flow) {
if(src == des) return flow;
int ans = 0;
for(int i=head[src]; ~i; i=node[i].next) {
int v = node[i].v;
if(dep[v] == dep[src]+1 && node[i].w) {
int tmp = dfs(v, des, min(node[i].w, flow-ans));
node[i].w -= tmp;
node[i^1].w += tmp;
ans += tmp;
if(ans == flow) return ans;
}
}
if(!ans) dep[src] = -2;
return ans;
}
int dinic(int src, int des) {
int ans = 0, tmp;
while(bfs(src, des)) {
while((tmp = dfs(src, des, inf)) > 0) {
ans += tmp;
}
}
return ans;
}
int main() {
init();
scanf("%d%d%d%d", &n, &m, &A, &B);
int src = 0, des = n*m+1;
for(int i=1; i<=n; i++) {
scanf("%s", s[i]+1);
for(int j=1; j<=m; j++) {
if(s[i][j] == '.') {
addnode(src, getid(i, j), B);
addnode(getid(i, j), src, 0);
} else {
addnode(getid(i, j), des, B);
addnode(des, getid(i, j), 0);
}
}
}
for(int i=1; i<=n; i++) {
for(int j=1; j<=m; j++) {
for(int k=0; k<4; k++) {
int x = i+dis[k][0];
int y = j+dis[k][1];
if(x<1 || y<1 || x>n || y>m) continue;
addnode(getid(i, j), getid(x, y), A);
addnode(getid(x, y), getid(i, j), 0);
}
}
}
int ans = dinic(src, des);
printf("%d
", ans);
return 0;
}