zoukankan      html  css  js  c++  java
  • [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;
    }
    
  • 相关阅读:
    Qt之界面数据存储与获取(使用setUserData()和userData())
    UML中关联(Association)、聚合(Aggregation)和合成(Composition)之间的区别
    Entity Framework Model First下改变数据库脚本的生成方式
    keepalive学习
    函数、极限、连续
    C#集合基础与运用
    面向查询服务的参数化查询
    WinDbg 命令手册
    知识管理方法论
    项目管理Project
  • 原文地址:https://www.cnblogs.com/crashed/p/14243799.html
Copyright © 2011-2022 走看看