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;
}
posted @ 2017-11-16 14:39  zjp_shadow  阅读(278)  评论(0编辑  收藏  举报