• [NOI2008] 志愿者招募


    题目

    点这里看题目。

    分析

    最初,我们可以猜想直接将志愿者需求 (a_i) 当作容量建边;但问题也很显然,即一个志愿者流量只有 1 ,我们却需要他的流量在多余一条边中被计算。

    此时就需要更换思路:我们要做减法。平时我们通过流的叠加并达到流量上界满足要求,现在我们要求将流退掉,从而在最大流的前提下迫使流走其它路径计算花费

    在这个问题中,我们不将志愿者需求看作上界,而是将它看作是流过 (i) 时,必须少流 (a_i) 的流量,而 (a_i) 的流量就应该走其他路,也即是计算花费的 " 志愿者路径 " 。

    建图如下:

    寻找一个充分大的量 (F) ,构建 (n+1) 个点和 (s,t)

    对于 (ile n) ,连接 (i ightarrow i+1) ,容量为 (F-a_i) ,费用为 (0)

    对于志愿者 ((s,t,c)) ,连接 (s ightarrow t+1) ,容量为 (+infty) ,费用为 (c)

    连接 (s ightarrow 1) ,容量为 (F) ;连接 (n+1 ightarrow t) ,容量为 (F)

    另外,此题还有线性规划做法,等我学习之后再来写吧

    小结:

    1. 转化思路,强制流走其它路径从而计算花费的方法值得借鉴。
    2. " 链式 " 建图方法可以表示连续的一段状态,在处理区间相关问题是颇为有效。

    代码

    #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) )
    
    typedef long long LL;
    
    #define int LL
    
    const int INF = 0x3f3f3f3f3f3f3f3f;
    const int MAXN = 2e5 + 5, MAXM = 2e5 + 5;
    
    template<typename _T>
    void read( _T &x )
    {
    	x = 0; char s = getchar(); int f = 1;
    	while( s < '0' || '9' < s ) { f = 1; if( s == '-' ) f = -1; s = getchar(); }
    	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ); s = getchar(); }
    	x *= f;
    }
    
    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 MIN( const _T a, const _T b )
    {
    	return a < b ? a : b;
    }
    
    template<typename _T>
    _T MAX( const _T a, const _T b )
    {
    	return a > b ? a : b;
    }
    
    struct Edge
    {
    	int to, nxt, w, c;
    }Graph[MAXM << 1];
    
    int q[MAXN], fro, rea;
    
    int A[MAXN];
    
    int head[MAXN], dist[MAXN], cur[MAXN];
    int N, M, cnt = 1, tot;
    bool vis[MAXN];
    
    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;
    }
    
    void AddE( const int from, const int to, const int C, const int W ) { AddEdge( from, to, C, W ), AddEdge( to, from, 0, -W ); }
    
    #define Nxt( x ) ( x = ( x + 1 ) % MAXN )
    
    bool SPFA( const int S, const int T )
    {
    	int u, v; fro = rea = 0;
    	rep( i, 1, tot ) dist[i] = INF, vis[i] = false;
    	vis[q[rea] = S] = true, dist[S] = 0, Nxt( rea );
    	while( fro ^ rea )
    	{
    		vis[u = q[fro]] = false, Nxt( fro );
    		for( int i = head[u] ; 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] ) vis[q[rea] = v] = true, Nxt( rea );
    			}
    	}
    	return dist[T] < INF;
    }
    
    int DFS( const int u, const int lin, const int T, int &cost )
    {
    	if( u == T ) return lin;
    	int used = 0, ret, v, c, w; vis[u] = true;
    	for( int &i = cur[u] ; i ; i = Graph[i].nxt )
    	{
    		v = Graph[i].to, c = Graph[i].c, w = Graph[i].w;
    		if( dist[v] == dist[u] + w && c && ! vis[v] && ( ret = DFS( v, MIN( lin - used, c ), T, cost ) ) )
    		{
    			used += ret, Graph[i].c -= ret, Graph[i ^ 1].c += ret, cost += ret * w;
    			if( used == lin ) break;
    		}
    	}
    	if( used < lin ) dist[u] = INF;
    	vis[u] = false; return used;
    }
    
    int Dinic( const int S, const int T )
    {
    	int ret = 0;
    	while( SPFA( S, T ) )
    	{
    		rep( i, 1, tot ) cur[i] = head[i], vis[i] = false;
    		DFS( S, INF, T, ret );
    	}
    	return ret;
    }
    
    signed main()
    {
    	read( N ), read( M ), tot = N + 1;
    	int inf = N * M;
    	const int s = ++ tot, t = ++ tot;
    	rep( i, 1, N ) read( A[i] );
    	rep( i, 1, N ) AddE( i, i + 1, inf - A[i], 0 );
    	rep( i, 1, M ) { int fr, to, c;
    		read( fr ), read( to ), read( c );
    		AddE( fr, to + 1, inf, c );
    	}
    	AddE( s, 1, inf, 0 ), AddE( N + 1, t, inf, 0 );
    	write( Dinic( s, t ) ), putchar( '
    ' );
    	return 0;
    }
    
  • 相关阅读:
    Java实现 蓝桥杯VIP 算法训练 一元三次方程
    Java实现 蓝桥杯VIP 算法训练 乘法表
    Java实现 蓝桥杯VIP 算法训练 矩阵加法
    Java实现 蓝桥杯VIP 算法训练 一元三次方程
    Java实现 蓝桥杯VIP 算法训练 平方计算
    Java实现 蓝桥杯VIP 算法训练 平方计算
    Java实现 蓝桥杯VIP 算法训练 平方计算
    Java实现 蓝桥杯VIP 算法训练 乘法表
    Java实现 蓝桥杯VIP 算法训练 乘法表
    监管只是压倒网盘业务的一根稻草,但不是主要原因(答案只有一个:成本!)
  • 原文地址:https://www.cnblogs.com/crashed/p/14243799.html
Copyright © 2020-2023  润新知