题目描述
分析
看完题目居然没什么思路,菜的真实233 哎这显然是一个01分数规划吧首先。什么?你不会01分数规划?好吧我也不太会了,我们来回顾一下。 对于题目的这个式子,我们转化为更一般的:
∑
i
=
1
n
w
i
∗
x
i
∑
i
=
1
n
x
i
\frac{\sum_{i=1}^nw_i*x_i}{\sum_{i=1}^nx_i}
∑ i = 1 n x i ∑ i = 1 n w i ∗ x i ,其中
x
i
x_i
x i 为0或1,我们让这个式子最小。我们假设一个L,如果存在一组解,使
∑
i
=
1
n
w
i
∗
x
i
∑
i
=
1
n
x
i
<
=
L
\frac{\sum_{i=1}^nw_i*x_i}{\sum_{i=1}^nx_i}<=L
∑ i = 1 n x i ∑ i = 1 n w i ∗ x i < = L ,那么L一定大于答案。我们发现答案具有单调性,因此可以二分。 下面是式子的转化:
∑
i
=
1
n
w
i
∗
x
i
∑
i
=
1
n
x
i
<
=
L
\frac{\sum_{i=1}^nw_i*x_i}{\sum_{i=1}^nx_i}<=L
∑ i = 1 n x i ∑ i = 1 n w i ∗ x i < = L
=
>
∑
i
=
1
n
(
w
i
−
L
)
∗
x
i
<
=
0
=> \sum_{i=1}^n(w_i-L)*x_i<=0
= > i = 1 ∑ n ( w i − L ) ∗ x i < = 0 。 这样的话,有思路了吧。每次二分一个L,把边权减去L,找出s到t的最小割,如果最小割+边权为负的权值<=0,那么说明L不是答案,让r=mid。否则,让l=mid。 思路就讲到这里,具体细节,自己实现的时候注意一下。
Coding
#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cmath>
#define Maxn 23333
#define Maxe 23333
using namespace std;
inline int Getint ( ) { int x= 0 , f= 1 ; char ch= getchar ( ) ; while ( '0' > ch|| ch> '9' ) { if ( ch== '-' ) f= - 1 ; ch= getchar ( ) ; } while ( '0' <= ch&& ch<= '9' ) { x= x* 10 + ch- '0' ; ch= getchar ( ) ; } return x* f; }
int n, m, S, T, N, cnt= 0 , x[ Maxn] , y[ Maxn] , v[ Maxn] , h[ Maxn] , GAP[ Maxn] , dis[ Maxn] ;
bool vis[ Maxn] , used[ Maxn] ;
struct node{ int to, next, v, pair; } e[ Maxe] ;
void AddEdge ( int X, int Y, int v, int pa) { e[ cnt] = ( node) { Y, h[ X] , v, pa} ; h[ X] = cnt; }
void AddEdge ( int X, int Y, int v) { AddEdge ( X, Y, v, ++ cnt+ 1 ) ; AddEdge ( Y, X, 0 , ++ cnt- 1 ) ; }
int SAP ( int X, int Maxflow) {
if ( X== T) return Maxflow;
int tmp= Maxflow;
for ( int p= h[ X] ; p; p= e[ p] . next) {
int y= e[ p] . to;
int flow= min ( tmp, e[ p] . v) ;
if ( flow&& dis[ X] == dis[ y] + 1 ) {
int ret= SAP ( y, flow) ;
tmp- = ret;
e[ p] . v- = ret;
e[ e[ p] . pair] . v+ = ret;
if ( ! tmp|| dis[ S] == n) return Maxflow- tmp;
}
}
if ( -- GAP[ dis[ X] ] == 0 ) dis[ S] = n;
else GAP[ ++ dis[ X] ] ++ ;
return Maxflow- tmp;
}
double SAP ( ) {
memset ( GAP, 0 , sizeof ( GAP) ) ;
memset ( dis, 0 , sizeof ( dis) ) ;
GAP[ 0 ] = n;
S= 1 , T= n;
int Ans= 0 ;
while ( dis[ S] < n) Ans+ = SAP ( S, 1 << 30 ) ;
return Ans;
}
bool Check ( double k) {
memset ( e, 0 , sizeof ( e) ) ;
memset ( h, 0 , sizeof ( h) ) ;
memset ( used, 0 , sizeof ( vis) ) ;
cnt= 0 ;
double ret= 0.0 ;
for ( int i= 1 ; i<= m; i++ ) {
if ( v[ i] <= k) { used[ i] = 1 ; ret+ = v[ i] - k; }
else { AddEdge ( x[ i] , y[ i] , v[ i] - k) ; AddEdge ( y[ i] , x[ i] , v[ i] - k) ; }
}
return SAP ( ) + ret> 0 ;
}
void Init ( ) {
for ( int i= 1 ; i<= m; i++ ) x[ i] = Getint ( ) , y[ i] = Getint ( ) , v[ i] = Getint ( ) ;
}
void Dfs ( int x) {
vis[ x] = true ;
for ( int p= h[ x] ; p; p= e[ p] . next) {
int y= e[ p] . to;
if ( ! vis[ y] && e[ p] . v) Dfs ( y) ;
}
}
void Solve ( ) {
double L= 0 , r= 1e9 ;
while ( L+ 1e-8 < r) {
double mid= ( L+ r) / 2 ;
if ( Check ( mid) ) L= mid;
else r= mid;
}
memset ( vis, 0 , sizeof ( vis) ) ;
Dfs ( S) ;
for ( int i= 1 ; i<= m; i++ ) if ( vis[ x[ i] ] != vis[ y[ i] ] ) used[ i] = true ;
int Ans= 0 ;
for ( int i= 1 ; i<= m; i++ ) Ans+ = used[ i] ;
cout<< Ans<< "\n" ;
for ( int i= 1 ; i<= m; i++ ) if ( used[ i] ) cout<< i<< " " ;
cout<< "\n" ;
}
int main ( ) {
while ( scanf ( "%d%d" , & n, & m) != EOF ) {
Init ( ) ;
Solve ( ) ;
}
return 0 ;
}