「POJ2175」Evacuation Plan

题目

点这里看题目。

分析

显而易见的方法:直接建图跑一个最小费用最大流,然后比较自己得到的结果和给出的方案。

但是这里有 \(O(n^2)\) 条边,再加上流量可以被构造得很大,因此这种方法不出所料地超时了。

注意到,题目给出的方案一定是一个最大流的方案,但不一定是最小费用的方案。换句话说,题目其实给出了一个残量网络。根据完全不众所周知的消圈原理,如果这个残量网络上存在负环,那么这个流就一定不是最小费用流。另外,如果搜索到了一个负环,我们也可以在这个负环上调整 1 的流量,从而得到一种更优的方案,因此也可以很容易地构造出方案。

理论的复杂度应为 \(O(n^2m)\),勉强能够过去。如果使用 DFS 版本的 SPFA 并加上若干优化,那么就能跑得飞快。

补充一下消圈算法:

消圈算法就是几乎直接由消圈原理得出的算法。

我们首先找出一个合法的最大流方案,而后不断地调整负环,使得残量网络上再也找不出负环,就得到了最小费用最大流。

不同于常用的 SSP 方法导出的算法,无论原网络上有无负环,这个算法都可以正常地运行。

据传,精细实现后的消圈算法,复杂度可以降到 \(O(nm^2\log n)\),这个算法是多项式的!

小结:仔细观察给出信息的含义,并充分利用它

代码

#include <queue>
#include <cstdio>

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

const int INF = 0x3f3f3f3f;
const int MAXN = 1005, MAXE = 1e5 + 5;

template<typename _T>
void read( _T &x ) {
    x = 0; char s = getchar(); bool f = false;
    while( s < '0' || '9' < s ) { f = s == '-', s = getchar(); }
    while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar(); }
    if( f ) x = -x;
}

template<typename _T>
void write( _T x ) {
    if( x < 0 ) putchar( '-' ), x = -x;
    if( 9 < x ) write( x / 10 );
    putchar( x % 10 + '0' );
}

template<typename _T>
_T ABS( const _T a ) {
    return a < 0 ? -a : a;
}

struct Edge {
    int to, nxt, c, w;
}Graph[MAXE << 1];

int cir[MAXN], tot;
int stk[MAXN], top;

int dist[MAXN], dep[MAXN];
bool vis[MAXN];

int head[MAXN];
int ntot, cnt = 1;

int E[105][105];
int X[MAXN], Y[MAXN], B[MAXN];
int P[MAXN], Q[MAXN], C[MAXN];

int N, M;

inline void AddEdge( const int from, const int to, const int C, const int W ) {
    Graph[++ cnt].to = to, Graph[cnt].nxt = head[from];
    Graph[cnt].c = C, Graph[cnt].w = W, head[from] = cnt;
}

bool DFS( const int u ) {
    vis[u] = true;
    stk[dep[u] = ++ top] = u;
    for( int i = head[u], v ; i ; i = Graph[i].nxt )
        if( Graph[i].c && dist[v = Graph[i].to] > dist[u] + Graph[i].w ) {
            dist[v] = dist[u] + Graph[i].w;
            if( ! vis[v] ) {
                if( DFS( v ) )
                    return true;
            }
            else {
                rep( j, dep[v], dep[u] )
                    cir[++ tot] = stk[j];
                return true;
            }
        }
    vis[u] = false, top --;
    return false;
}

int main() {
    read( N ), read( M ), ntot = N + M;
    const int s = ++ ntot, t = ++ ntot;
    rep( i, 1, N ) 
        read( X[i] ), read( Y[i] ), read( B[i] );
    rep( i, 1, M ) 
        read( P[i] ), read( Q[i] ), read( C[i] );
    rep( i, 1, N ) rep( j, 1, M ) {
        read( E[i][j] );
        int wei = ABS( X[i] - P[j] ) + ABS( Y[i] - Q[j] ) + 1;
        AddEdge( i, j + N, INF, wei ), AddEdge( j + N, i, E[i][j], - wei );
    }
    rep( i, 1, N ) {
        int su = 0;
        rep( j, 1, M ) su += E[i][j];
        AddEdge( s, i, B[i] - su, 0 );
        AddEdge( i, s, su, 0 );
    }
    rep( j, 1, M ) {
        int su = 0;
        rep( i, 1, N ) su += E[i][j];
        AddEdge( j + N, t, C[j] - su, 0 );
        AddEdge( t, j + N, su, 0 );
    }
    rep( i, 1, ntot ) {
        rep( j, 1, ntot ) dist[j] = 0, vis[j] = false;
        tot = top = dist[i] = 0; 
        if( DFS( i ) ) break;
    }
    if( tot == 0 ) puts( "OPTIMAL" );
    else {
        puts( "SUBOPTIMAL" );
        cir[++ tot] = cir[1];
        for( int i = 1 ; i < tot ; i ++ ) {
            int u = cir[i], v = cir[i + 1];
            if( u <= N && N < v && v <= N + M ) E[u][v - N] ++;
            if( v <= N && N < u && u <= N + M ) E[v][u - N] --;
        }
        rep( i, 1, N ) rep( j, 1, M )
            write( E[i][j] ), putchar( j == M ? '\n' : ' ' );
    }
    return 0;
}
原文地址:https://www.cnblogs.com/crashed/p/15518627.html