P2792 [JSOI2008]小店购物

给出一个易于理解且好写的做法。

有一个贪心做法,

  • 从源点向每一种商品连 \(c_i \times m_i\) 的边。

  • 对于优惠方案 \((A,B,P)\) , 从 \(A\)\(B\)\(P \times m_B\) 的边。

最后源点的最小树形图即为答案?

这样做显然有问题,因为如果同时存在优惠方案 \((A,B)\)\((B,A)\) ,那么先买一件 \(A\) , 再用优惠价买完 \(B\) , 然后用优惠价买完 \(A\),这种方案可能更优。

所以可以将一件商品拆成 \(2\) 个点,表示买 \(1\) 件和买 \(m_i-1\) 件,连边方式同上。

注意细节,不要多建新点。

#include <cstdio>
#include <cstring>
#define Inf 1e9

const int MAXN = 100 , MAXM = MAXN * ( MAXN + 1 );

int m;
struct Edge{ int u , v; double w; Edge(){} Edge( int U , int V , double W ) { u = U , v = V , w = W; } } Graph[ MAXM + 5 ];
void Add_Edge( int u , int v , double w ) { Graph[ ++ m ] = Edge( u , v , w ); }

int n , k , rt , num[ MAXN + 5 ];
struct node { int v; double w; node(){} node( int V , double W ) { v = V , w = W; } } pre[ MAXN + 5 ];
int vis[ MAXN + 5 ] , bel[ MAXN + 5 ];
double Zhuliu( int rt ) {
    double Ans = 0;
    while( 1 ) {
        for( int i = 1 ; i <= n ; i ++ ) pre[ i ] = node( 0 , Inf );
        for( int i = 1 ; i <= m ; i ++ ) {
            int u = Graph[ i ].u , v = Graph[ i ].v; double w = Graph[ i ].w;
            if( u != v && pre[ v ].w > w ) pre[ v ] = node( u , w );
        }
        for( int i = 1 ; i <= n ; i ++ ) if( i != rt && !pre[ i ].v ) return -1;
        
        int cnt = 0;
        memset( vis , 0 , sizeof( vis ) );
        memset( bel , 0 , sizeof( bel ) );
        for( int i = 1 , u ; i <= n ; i ++ ) {
            if( i == rt ) continue; Ans += pre[ i ].w;
            for( u = i ; u != rt && vis[ u ] != i ; vis[ u ] = i , u = pre[ u ].v );
            if( u != rt && !bel[ u ] ) {
                bel[ u ] = ++ cnt;
                for( ; u != rt && !bel[ pre[ u ].v ] ; u = pre[ u ].v , bel[ u ] = cnt );
            }
        }
        if( !cnt ) break;
        for( int i = 1 ; i <= n ; i ++ ) if( !bel[ i ] ) bel[ i ] = ++ cnt;
        for( int i = 1 ; i <= m ; i ++ ) {
            int u = Graph[ i ].u , v = Graph[ i ].v; double w = Graph[ i ].w;
            Graph[ i ].u = bel[ u ] , Graph[ i ].v = bel[ v ];
            if( bel[ u ] != bel[ v ] ) Graph[ i ].w = w - pre[ v ].w;
        }
        n = cnt; rt = bel[ rt ];
    }
    return Ans;
}

int cnt , id[ MAXN + 5 ][ 2 ];
double c;
int main( ) {
    scanf("%d",&n); rt = ++ cnt;
    for( int i = 1 ; i <= n ; i ++ ) {
        scanf("%lf %d",&c,&num[ i ]);
        if( num[ i ] > 0 ) id[ i ][ 0 ] = ++ cnt , Add_Edge( rt , id[ i ][ 0 ] , c );
        if( num[ i ] > 1 ) id[ i ][ 1 ] = ++ cnt , Add_Edge( rt , id[ i ][ 1 ] , c * ( num[ i ] - 1 ) );
    }
    scanf("%d",&k);
    for( int i = 1 , u , v ; i <= k ; i ++ ) {
        scanf("%d %d %lf",&u,&v,&c);
        if( num[ u ] > 0 && num[ v ] > 0 ) Add_Edge( id[ u ][ 0 ] , id[ v ][ 0 ] , c );
        if( num[ u ] > 0 && num[ v ] > 1 ) Add_Edge( id[ u ][ 0 ] , id[ v ][ 1 ] , c * ( num[ v ] - 1 ) );
        if( num[ u ] > 1 && num[ v ] > 0 ) Add_Edge( id[ u ][ 1 ] , id[ v ][ 0 ] , c );
        if( num[ u ] > 1 && num[ v ] > 1 ) Add_Edge( id[ u ][ 1 ] , id[ v ][ 1 ] , c * ( num[ v ] - 1 ) );
    }
    n = cnt;
    printf("%.2f\n", Zhuliu( rt ) );
    return 0;
}
posted @ 2021-04-08 21:06  chihik  阅读(40)  评论(0编辑  收藏  举报