【bzoj3993】[SDOI2015]星际战争 二分+最大流

题目描述

3333年,在银河系的某星球上,X军团和Y军团正在激烈地作战。在战斗的某一阶段,Y军团一共派遣了N个巨型机器人进攻X军团的阵地,其中第i个巨型机器人的装甲值为Ai。当一个巨型机器人的装甲值减少到0或者以下时,这个巨型机器人就被摧毁了。X军团有M个激光武器,其中第i个激光武器每秒可以削减一个巨型机器人Bi的装甲值。激光武器的攻击是连续的。这种激光武器非常奇怪,一个激光武器只能攻击一些特定的敌人。Y军团看到自己的巨型机器人被X军团一个一个消灭,他们急需下达更多的指令。为了这个目标,Y军团需要知道X军团最少需要用多长时间才能将Y军团的所有巨型机器人摧毁。但是他们不会计算这个问题,因此向你求助。

输入

第一行,两个整数,N、M。

第二行,N个整数,A1、A2…AN。
第三行,M个整数,B1、B2…BM。
接下来的M行,每行N个整数,这些整数均为0或者1。这部分中的第i行的第j个整数为0表示第i个激光武器不可以攻击第j个巨型机器人,为1表示第i个激光武器可以攻击第j个巨型机器人。

输出

 一行,一个实数,表示X军团要摧毁Y军团的所有巨型机器人最少需要的时间。输出结果与标准答案的绝对误差不超过10-3即视为正确。

样例输入

2 2
3 10
4 6
0 1
1 1

样例输出

1.300000


题解

二分+最大流,apio讲过的题。

设激光武器为xi,机器人为yi。

二分时间mid,S->xi,容量为bi*mid(需要保留小数);xi->能够攻击的yi,容量为inf;yi->T,容量为ai。

判断是否满流,并更新答案。

由于二分的时间是小数,所以最好是限制迭代次数,达到次数后跳出循环即可。

#include <cstdio>
#include <cstring>
#include <queue>
#define N 110
#define M 100000
#define inf 1000000000
using namespace std;
queue<int> q;
double a[N] , b[N] , val[M];
int n , m , p[N][N] , head[N] , to[M] , next[M] , cnt = 1 , s , t , dis[N];
void add(int x , int y , double z)
{
    to[++cnt] = y , val[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
    to[++cnt] = x , val[cnt] = 0 , next[cnt] = head[y] , head[y] = cnt;
}
bool bfs()
{
    int x , i;
    memset(dis , 0 , sizeof(dis));
    while(!q.empty()) q.pop();
    dis[s] = 1 , q.push(s);
    while(!q.empty())
    {
        x = q.front() , q.pop();
        for(i = head[x] ; i ; i = next[i])
        {
            if(val[i] > 0 && !dis[to[i]])
            {
                dis[to[i]] = dis[x] + 1;
                if(to[i] == t) return 1;
                q.push(to[i]);
            }
        }
    }
    return 0;
}
double dinic(int x , double low)
{
    if(x == t) return low;
    double temp = low , k;
    int i;
    for(i = head[x] ; i ; i = next[i])
    {
        if(val[i] > 0 && dis[to[i]] == dis[x] + 1)
        {
            k = dinic(to[i] , min(temp , val[i]));
            if(k <= 0) dis[to[i]] = 0;
            val[i] -= k , val[i ^ 1] += k;
            if((temp -= k) <= 0) break;
        }
    }
    return low - temp;
}
bool judge(double mid)
{
    int i , j;
    double sum = 0;
    memset(head , 0 , sizeof(head)) , cnt = 1;
    for(i = 1 ; i <= m ; i ++ ) add(s , i , b[i] * mid);
    for(i = 1 ; i <= n ; i ++ ) add(i + m , t , a[i]) , sum += a[i];
    for(i = 1 ; i <= m ; i ++ )
        for(j = 1 ; j <= n ; j ++ )
            if(p[i][j])
                add(i , j + m , inf);
    while(bfs()) sum -= dinic(s , inf);
    return sum <= 0;
}
int main()
{
    int i , j , tot = 100;
    double l = 0 , r = 1000000 , mid;
    scanf("%d%d" , &n , &m) , s = 0 , t = n + m + 1;
    for(i = 1 ; i <= n ; i ++ ) scanf("%lf" , &a[i]);
    for(i = 1 ; i <= m ; i ++ ) scanf("%lf" , &b[i]);
    for(i = 1 ; i <= m ; i ++ )
        for(j = 1 ; j <= n ; j ++ )
            scanf("%d" , &p[i][j]);
    while(tot -- )
    {
        mid = (l + r) / 2;
        if(judge(mid)) r = mid;
        else l = mid;
    }
    printf("%.6lf\n" , r);
    return 0;
}

 

 

posted @ 2017-06-13 09:06  GXZlegend  阅读(259)  评论(0编辑  收藏  举报