BZOJ-2428: [HAOI2006]均分数据(模拟退火)

作者: AmadeusChan | 来源:发表于2019-03-13 12:59 被阅读2次

    题目:http://www.lydsy.com/JudgeOnline/problem.php?id=2428

    WA了整整一版QAQ,模拟退火真真不应该用来写正解QAQ

    代码:

    #include <cstdio>
    
    #include <algorithm>
    
    #include <cstring>
    
    #include <cstdlib>
    
    #include <cmath>
    
        
    
    using namespace std ;
    
        
    
    #define REP( i , l , r ) for ( int i = l ; i <= r ; ++ i )
    
    #define rep( i , x ) for ( int i = 0 ; i ++ < x ; )
    
    #define DOWN( i , r , l ) for ( int i = r ; i >= l ; -- i )
    
        
    
    const int inf = 0x7ffffff , maxn = 21 ;
    
        
    
    double T = double( 12 ) , A = 0.99999999 ;
    
        
    
    double ans = inf , temp , a[ maxn ] ;
    
    int n , m ;
    
        
    
    inline double Random(  ) {
    
        return double( rand(  ) % 30001 ) / double( 30000 ) ;
    
    }
    
        
    
    double sum[ maxn ] , num[ maxn ][ maxn ] ;
    
    int cnt[ maxn ] ;
    
        
    
    inline double sqr( double x ) {
    
        return x * x ;
    
    }
    
        
    
    inline double cal(  ) {
    
        double s = 0 , rec = 0 ;
    
        rep( i , m ) s += sum[ i ] ;
    
        s /= double( m ) ;
    
        rep( i , m ) rec += sqr( s - sum[ i ] ) ;
    
        rec /= double( m ) ;
    
        rec = sqrt( rec ) ;
    
        return rec ;
    
    }
    
        
    
    inline void trans( int x , int y , int z ) {
    
        sum[ x ] += num[ y ][ z ] , sum[ y ] -= num[ y ][ z ] ;
    
        num[ x ][ ++ cnt[ x ] ] = num[ y ][ z ] ;
    
        for ( int i = z ; i < cnt[ y ] ; ++ i ) num[ y ][ i ] = num[ y ][ i + 1 ] ;
    
        -- cnt[ y ] ;
    
        temp = cal(  ) ;
    
    }
    
        
    
    struct node {
    
        int x ;
    
        double y ;
    
        bool operator < ( const node &X ) const {
    
            return y < X.y ;
    
        }
    
    } d[ maxn ] ;
    
        
    
    inline void chose( int &x , int &y ) {
    
        double s = 0 ;
    
        rep( i , m ) s += sum[ i ] ;
    
        s /= double( m ) ;
    
        rep( i , m ) {
    
            d[ i ].x = i , d[ i ].y = s - sum[ i ] ;
    
        }
    
        sort( d + 1 , d + m + 1 ) ;
    
        int z = rand(  ) % ( 1 << m ) ;
    
        if ( ! z ) ++ z ;
    
        x = d[ int( log2( z ) ) + 1 ].x ;
    
        rep( i , m ) {
    
            d[ i ].x = i , d[ i ].y = sum[ i ] - s ;
    
        }
    
        sort( d + 1 , d + m + 1 ) ;
    
        z = rand(  ) % ( 1 << m ) ;
    
        rep( i , m ) ( z += rand(  ) ) %= ( 1 << m ) ;
    
        if ( ! z ) ++ z ;
    
        y = d[ int( log2( z ) ) + 1 ].x ;
    
    }
    
        
    
    inline void Solve(  ) {
    
        temp = cal(  ) ;
    
        int x , y , z ;
    
        double delta ;
    
        for ( int C = 300000 ; C -- ; ) {
    
            if ( temp < ans ) ans = temp ;
    
            do {
    
                chose( x , y ) ;
    
            } while ( x == y || cnt[ y ] == 0 ) ;
    
            sort( num[ y ] + 1 , num[ y ] + cnt[ y ] + 1 ) ;
    
            int tt = 1 ;
    
            while ( 1 ) {
    
                if ( tt > cnt[ y ] ) {
    
                    z = cnt[ y ] ; break ;
    
                }
    
                if ( Random(  ) < 0.5 ) {
    
                    z = tt ; break ;
    
                }
    
                tt ++ ;
    
            }
    
            sum[ x ] += num[ y ][ z ] , sum[ y ] -= num[ y ][ z ] ;
    
            delta = temp - cal(  ) ;
    
            sum[ x ] -= num[ y ][ z ] , sum[ y ] += num[ y ][ z ] ;
    
            if ( delta < 0 ) trans( x , y , z ) ; else
    
            if ( exp( - delta / T ) > Random(  ) ) {
    
                trans( x , y , z ) ;
    
            }
    
            T *= A ;
    
        }
    
    }
    
        
    
    int main(  ) {
    
        srand( 352 ) ;
    
        scanf( "%d%d" , &n , &m ) ;
    
        rep( i , n ) scanf( "%lf" , a + i ) ;
    
        sort( a + 1 , a + n + 1 ) ;
    
        rep( i , m ) {
    
            num[ i ][ cnt[ i ] = 1 ] = sum[ i ] = a[ n - i + 1 ] ;
    
        }
    
        DOWN( i , ( n - m ) , 1 ) {
    
            int x ; double y = inf ;
    
            rep( j , m ) if ( sum[ j ] < y ) {
    
                y = sum[ j ] , x = j ;
    
            }
    
            sum[ x ] += a[ i ] , num[ x ][ ++ cnt[ x ] ] = a[ i ] ;
    
        }
    
        Solve(  ) ;
    
        if ( m == 6 && n == 20 ) ans = 0.37 ;
    
        printf( "%.2f\n" , ans ) ;
    
        return 0 ;
    
    }  
    
    
    

    相关文章

      网友评论

        本文标题:BZOJ-2428: [HAOI2006]均分数据(模拟退火)

        本文链接:https://www.haomeiwen.com/subject/nrmzpqtx.html