BZOJ 1001: [BeiJing2006]狼抓兔子 (最小割)
虽然联赛考崩了,但还是要继续坚持下去啊,以后就去BZOJ上刷题吧QwQ
题目大意
给你一个\(n*m\)的矩形平面图,斜边和直边上都有权值,选一些边,使\((1,1)\)和\((n,m)\)分开,使这些边权值和最小!
解题思路
比较明显的一个最小割,和[ZJOI2009]狼和羊的故事很像的,都是将两个联通块分开,问分开边权和的最小值。
一开始调了比较久,答案都小了,因为我没有将从下向上走的边连上去,这种就会使答案偏小,所以以后要多注意一下这种最小割的题要连两条边。
而且这种题目好像也不用拆点。。所以一开始有点傻,拆了点做的所以\(GetPoint\)会多一个\(id\)自动忽略掉吧。。
好像最小割并不是正解,但进行一些优化能卡过去(参考了下hzwer大佬的)。
正解使S-T平面图上求最小割,具体参考这篇论文
像那种双向边,可以都连边权值大小的边,没必要连反向边了(因为可以把它们当做互为反向边)。
代码
/**************************************************************
Problem: 1001
User: zjp_shadow
Language: C++
Result: Accepted
Time:1512 ms
Memory:84972 kb
****************************************************************/
#include <bits/stdc++.h>
#define For(i, l, r) for(register int i = (l), _end_ = (int)(r); i <= _end_; ++i)
#define Fordown(i, r, l) for(register int i = (r), _end_ = (int)(l); i >= _end_; --i)
#define Set(a, v) memset(a, v, sizeof(a))
using namespace std;
bool chkmin(int &a, int b) {return b < a ? a = b, 1 : 0;}
bool chkmax(int &a, int b) {return b > a ? a = b, 1 : 0;}
inline int read() {
int x = 0, fh = 1; char ch = getchar();
for (; !isdigit(ch); ch = getchar() ) if (ch == '-') fh = -1;
for (; isdigit(ch); ch = getchar() ) x = (x<<1) + (x<<3) + (ch ^ '0');
return x * fh;
}
void File() {
#ifdef zjp_shadow
freopen ("1001.in", "r", stdin);
freopen ("1001.out", "w", stdout);
#endif
}
const int N = 1010 * 1010, M = N * 6;
int to[M], Next[M], Head[N], cap[M], e = 1;
inline void add_edge (int u, int v, int w) {
to[++e] = v;
Next[e] = Head[u];
cap[e] = w;
Head[u] = e;
}
int S, T;
int r, c;
const int inf = 0x3f3f3f3f;
inline int Get_Point(int x, int y, int id) { return (x - 1) * c + y; }
int dis[N], n;
inline bool Bfs() {
Set(dis, 0);
queue<int> Q;
Q.push(S);
dis[S] = 1;
while (!Q.empty() ) {
register int u = Q.front(); Q.pop();
for (register int i = Head[u]; i; i = Next[i])
if (!dis[to[i]] && cap[i]) {
register int v = to[i];
dis[v] = dis[u] + 1;
if (v == T) return true;
Q.push(v);
}
}
return false;
}
int cur[N];
int Dfs(int u, int max_flow) {
if (u == T || !max_flow) return max_flow;
register int flow = 0, f;
for (register int& i = cur[u]; i; i = Next[i]) {
if ((dis[u] + 1 == dis[to[i]]) && (f = Dfs(to[i], min(max_flow, cap[i]) ) ) ) {
cap[i] -= f;
cap[i ^ 1] += f;
flow += f;
max_flow -= f;
if (!max_flow) break ;
}
}
if (!flow) dis[u] = 0;
return flow;
}
inline int Dinic() {
int sum_flow = 0;
while (Bfs() ) {
For (i, 1, n)
cur[i] = Head[i];
sum_flow += Dfs(S, inf);
}
return sum_flow;
}
int main () {
File() ;
r = read(), c = read();
For (i, 1, r)
For (j, 1, c - 1) {
static int u, v, w;
u = Get_Point(i, j, 1);
v = Get_Point(i, j + 1, 0);
w = read() ;
add_edge (u, v, w);
add_edge (v, u, w);
}
For (i, 1, r - 1)
For (j, 1, c) {
static int u, v, w;
u = Get_Point(i, j, 1);
v = Get_Point(i + 1, j, 0);
w = read() ;
add_edge (u, v, w);
add_edge (v, u, w);
}
For (i, 1, r - 1)
For (j, 1, c - 1) {
static int u, v, w;
u = Get_Point(i, j, 1);
v = Get_Point(i + 1, j + 1, 0);
w = read() ;
add_edge (u, v, w);
add_edge (v, u, w);
}
S = Get_Point(1, 1, 0);
T = Get_Point(r, c, 1);
n = T;
printf ("%d\n", Dinic() ) ;
return 0;
}