BZOJ1001: [BeiJing2006]狼抓兔子 (最小割)

传送门
题目大意:求给出图的最小割(……题目就是这个意思)。
明眼一看,这就是一个裸的最大流,可是被数据范围吓住了,有106个点,然后又有3106条边,对于网络流可怜的O(n2m)的时间复杂度来说,简直是怪兽……但之前有同学告诉我,这道题就是最大流,我就硬着头皮上了一个ISAP,光荣超时,然后去找题解(想不出来了……)发现了两种,一种是把每一个面当做一个点,然后做最短路,即得最小割的容量,另一种竟然还是硬上网络流……黄学长用的dinic只用了800ms,而我之前的ISAP却用了15000+ms这不科学啊。然后我尝试着预标号,然后再ISAP,竟然只要500ms,太神了……
以后一定记住,要先预处理一遍!

代码:

/**************************************************************
    Problem: 1001
    User: geng4512
    Language: C++
    Result: Accepted
    Time:552 ms
    Memory:87896 kb
****************************************************************/

#include<cstdio>
#include<cstring>
const int MAXN = 1001*1001;
inline int min(int a, int b) {return a < b ? a : b;}
int ecnt = -1;
struct node {
    int v, w, nxt;
}Edge[MAXN*6];
int Adj[MAXN], q[MAXN];
bool vis[MAXN];
void Addedge(int u, int v, int w) {
    node *t = &Edge[++ ecnt];
    t->v = v; t->w = w; t->nxt = Adj[u];
    Adj[u] = ecnt;
    t = &Edge[++ ecnt];
    t->v = u; t->w = w; t->nxt = Adj[v];
    Adj[v] = ecnt;
}
char c;
inline void GET(int &n) {
    n = 0;
    do c = getchar(); while(c > '9' || c < '0');
    while('0' <= c && c <= '9') {n=n*10+c-'0';c=getchar();}
}
int n, m, ans, S, T, d[MAXN], vd[MAXN], N;
int aug(int u, int augco) {
    int dmin = N-1, augc = augco, delta;
    if(u == T) return augco;
    for(int i = Adj[u]; ~i; i = Edge[i].nxt)
        if(Edge[i].w > 0) {
            if(d[Edge[i].v]+1 == d[u]) {
                delta = min(augc, Edge[i].w);
                delta = aug(Edge[i].v, delta);
                Edge[i].w -= delta;
                Edge[i^1].w += delta;
                augc -= delta;
                if(d[S] >= N) return augco - augc;
                if(!augc) return augco;
            }
            if(dmin > d[Edge[i].v]) dmin = d[Edge[i].v];
        }
    if(augco == augc) {
        -- vd[d[u]];
        if(!vd[d[u]]) d[S] = N;
        d[u] = ++ dmin;
        ++ vd[dmin];
    }
    return augco - augc;

}
void bfs() {
    int l = 0, r = 0, u;
    q[++ r] = T; vis[T] = 1; vd[0] ++;
    while(l < r) {
        u = q[++ l];
        for(int i = Adj[u]; ~i; i = Edge[i].nxt) {
            if(vis[Edge[i].v]) continue;
            d[Edge[i].v] = d[u] + 1;
            ++ vd[d[u]+1];
            vis[Edge[i].v] = 1;
            q[++r] = Edge[i].v;
        }
    }
}
void sap() {
    bfs();
    while(d[S] < N)
        ans += aug(S, 0x7fffffff);
}
int main()
{
    int w;
    memset(Adj, -1, sizeof Adj);
    GET(n); GET(m);
    for(int i = 0; i < n; ++ i)
    for(int j = 0; j < m-1; ++ j) {
        GET(w);
        Addedge(i*m+j, i*m+j+1, w);
    }
    for(int i = 0; i < n-1; ++ i)
    for(int j = 0; j < m; ++ j) {
        GET(w);
        Addedge(i*m+j, (i+1)*m+j, w);
    }
    for(int i = 0; i < n-1; ++ i)
    for(int j = 0; j < m-1; ++ j) {
        GET(w);
        Addedge(i*m+j, (i+1)*m+j+1, w);
    }
    T = n*m-1; N = n*m;
    sap();
    printf("%d\n", ans);
    return 0;
}

转载于:https://www.cnblogs.com/geng4512/p/5296896.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值