0w1

HR Spanning Tree Fraction ( 最優比例生成樹, Binary Search, Spanning Tree )

Programming Problems and Competitions :: HackerRank

題意:
N 個點 M 個邊,第 i 個邊用 a[ i ], b[ i ] 描述。求一個生成樹,使得 sum( a ) / sum( b ) 最大。

制約:
2 ≤ N ≤ 1e5
N - 1≤ M ≤ 1e5
1 ≤ a[ i ], b[ i ] ≤ 100

解法:
裸的最優比例生成樹。
聽說有這樣的東西也可以解這個,但我看不懂:
日月卦長的模板庫: [ 0-1 Fractional Programming Dinkelbach algorithm ] 0-1分數規劃
考慮對答案二分搜,假設為 x:
那麼條件可以改寫成:
x = sum( a ) / sum( b )
x * sum( b ) = sum( a )
考慮獨立:
x * b[ i ] = a[ i ]
那麼,取盡量多的 i 使得以下式子越大一定越好:
a[ i ] - x * b[ i ]
取完之後,如果發現總和為 0 以上,那這個 x 以上有解。
否則這個 x 以上無解。
看起來就很有單調性,嗯。

時間 / 空間複雜度:
O( M * lg ( M * max( a, b ) ) )

#include <bits/stdc++.h>
using namespace std;

const int MAXN = ( int ) 1e5;
const int MAXM = ( int ) 1e5;

int N, M;
int U[ MAXM ], V[ MAXM ], A[ MAXM ], B[ MAXM ];

int fa[ MAXM ];
int find( int x ){
  return fa[ x ] == x ? x : fa[ x ] = find( fa[ x ] );
}
bool unite( int x, int y ){
  int a = find( x );
  int b = find( y );
  if( a == b ) return 0;
  fa[ a ] = b;
  return 1;
}

int ord[ MAXM ];
double kruskal( double t, bool print ){
  for( int i = 0; i < M; ++i ){
    ord[ i ] = i;
  }
  sort( ord, ord + M, [ & ]( int i, int j ){
                        return 1.0 * A[ i ] - t * B[ i ] > 1.0 * A[ j ] - t * B[ j ];
                      } );
  for( int i = 0; i < M; ++i ){
    fa[ i ] = i;
  }
  int p = 0, q = 0;
  double res = 0.0;
  for( int i = 0; i < M; ++i ){
    if( unite( U[ ord[ i ] ], V[ ord[ i ] ] ) ){
      p += A[ ord[ i ] ];
      q += B[ ord[ i ] ];
      res += 1.0 * A[ ord[ i ] ] - t * B[ ord[ i ] ];
    }
  }
  if( print ){
    int g = __gcd( p, q );
    cout << p / g << "/" << q / g << endl;
  }
  return res;
}

signed main(){
  ios::sync_with_stdio( 0 );
  cin >> N >> M;
  for( int i = 0; i < M; ++i ){
    cin >> U[ i ] >> V[ i ] >> A[ i ] >> B[ i ];
  }
  double lb = 0.0, ub = 1e7;
  for( int i = 0; i < 100; ++i ){
    double mid = ( lb + ub ) / 2;
    if( kruskal( mid, 0 ) >= 0.0 ){
      lb = mid;
    } else{
      ub = mid;
    }
  }
  kruskal( lb, 1 );
  return 0;
}