[BZOJ3993]-[SDOI2015]星际战争-二分答案+最大流

说在前面

并没有什么想要说的,但是要保持格式


题目

BZOJ3993传送门
原题面描述很清楚
看题可以戳传送门


解法

可以发现,如果要在最短的时间内灭掉所有机器人,说明需要分配最优方案,使得造成的伤害能尽早达成要求
并且,如果在t时间内已经可以消灭所有机器人,那么t+1也一定可以,于是发现答案满足二分性

于是,问题就变成了,在限定的时间内(相当于限定了每个武器的输出),判断能否可行,于是就最大流check了


下面是自带大常数的代码

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std ;

const double inf = 1e10 , eps = 1e-8 ;
int N , M , acce[55][55] , head[105] , tp , S , T ;
double atk[55] , def[55] , mid , sum ;
struct Path{
    int pre , to ;
    double flow ;
}p[2*50*50+4*50+5] ;

template <typename T>
bool operator !( const T &A ){
    if( A < eps && A > -eps ) return true ;
    return false ;
}

void In( int t1 , int t2 , double t3 ){
    p[++tp] = ( Path ){ head[t1] , t2 , t3 } ;
    head[t1] = tp ;
}

void build(){
    tp = 1 ;
    memset( head , 0 , sizeof( head ) ) ;
    for( int i = 1 ; i <= M ; i ++ )
    for( int j = 1 ; j <= N ; j ++ )
        if( acce[i][j] ){
            In( i , j + M , inf ) ;
            In( j + M , i , 0 ) ;
        }
    for( int i = 1 ; i <= N ; i ++ ){
        In( i + M , T , def[i] ) ;
        In( T , i + M , 0 ) ;
    }
    for( int i = 1 ; i <= M ; i ++ ){
        In( S , i , mid * atk[i] ) ;
        In( i , S , 0 ) ;
    }
}

int dis[105] , que[105] , fr , ba ;
bool BFS(){
    memset( dis , -1 , sizeof( dis ) ) ;
    fr = 1 , ba = 0 ;
    dis[S] = 0 ; que[++ba] = S ;
    while( fr <= ba ){
        int u = que[fr++] ;
        for( int i = head[u] ; i ; i = p[i].pre ){
            int v = p[i].to ;
            if( dis[v] != -1 || !p[i].flow ) continue ;
            dis[v] = dis[u] + 1 ;
            que[++ba] = v ;
        }
    } return dis[T] != -1 ;
}

double dfs( int u , double flow ){
    if( u == T || !flow ) return flow ;
    double rt = 0 ;
    for( int i = head[u] ; i ; i = p[i].pre ){
        int v = p[i].to ;
        if( dis[v] != dis[u] + 1 || !p[i].flow ) continue ;
        double nowf = dfs( v , min( flow , p[i].flow ) ) ;
        if( !(!nowf) ){
            flow -= nowf ;
            rt += nowf ;
            p[i].flow -= nowf ;
            p[i^1].flow += nowf ;
            if( !flow ) break ;
        }
    } if( !rt || !(!flow) ) dis[u] = -1 ;
    return rt ;
}

bool check(){
    build() ;
    double tmp = sum ;
    while( BFS() )
        tmp -= dfs( S , inf ) ;
    return tmp <= 0 ;
}

void solve(){
    double lf = 0 , rg = 50*1e5 , ans ;
    while( rg - lf >= 5*1e-5 ){
        mid = ( rg + lf ) / 2 ;
        if( check() ){
            rg = ans = mid ;
        } else lf = mid ;
    }
    printf( "%f" , ans ) ;
}

int main(){
    scanf( "%d%d" , &N , &M ) ; S = N + M + 1 , T = S + 1 ;
    for( int i = 1 ; i <= N ; i ++ ) scanf( "%lf" , &def[i] ) , sum += def[i] ;
    for( int i = 1 ; i <= M ; i ++ ) scanf( "%lf" , &atk[i] ) ;
    for( int i = 1 ; i <= M ; i ++ )
        for( int j = 1 ; j <= N ; j ++ )
            scanf( "%d" , &acce[i][j] ) ;
    solve() ;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值