BZOJ3993 星际战争

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/C20190102/article/details/100040956

文章目录

题目

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

Input
第一行,两个整数, N N M M
第二行, N N 个整数, A 1 A_1 A 2 A_2 、…、 A N A_N
第三行, M M 个整数, B 1 B_1 B 2 B_2 、…、 B M B_M
接下来的 M M 行,每行 N N 个整数,这些整数均为0或者1。这部分中的第 i i 行的第 j j 个整数为0表示第 i i 个激光武器不可以攻击第 j j 个巨型机器人,为1表示第 i i 个激光武器可以攻击第 j j 个巨型机器人。

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

Sample Input
2 2
3 10
4 6
0 1
1 1

Sample Output
1.300000

Hint
战斗开始后的前0.5秒,激光武器1攻击2号巨型机器人,激光武器2攻击1号巨型机器人。1号巨型机器人被完全摧毁,2号巨型机器人还剩余8的装甲值;接下来的0.8秒,激光武器1、2同时攻击2号巨型机器人。2号巨型机器人被完全摧毁。

Data constraint
对于全部的数据, 1 N , M 50 1\leq N,M\leq50 1 A i 1 0 5 1\leq A_i\leq10^5 1 B i 1000 1\leq B_i\leq1000 ,输入数据保证X军团一定能摧毁Y军团的所有巨型机器人。

分析

看了提示才发现可以分配小数时间给每个敌人。
二分时间 t t ,注意精度问题。
每个武器是独立的,把源点向每个武器连流量为 t t 的边,这样就控制了它的攻击时间;每个武器 i i 向它能攻击的机器人 j j 连流量为 + +\infty 的边;每个机器人 j j 向汇点连流量为 B j A i \dfrac{B_j}{A_i} (即打爆它的时间)。跑Dinic就好了。

代码

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

int read(){
    int x=0;bool f=0;char c=getchar();
    while(c<'0'||c>'9') f|=c=='-',c=getchar();
    while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();
    return f?-x:x;
}

#define MAXN 100
#define INF 1e7
#define eps 1e-6
int N,M;
int A[MAXN+5],B[MAXN+5];
bool P[MAXN+5][MAXN+5];

struct Edge{
    double w;
    int v,Next;
    Edge(){}
    inline Edge(int x,double y,int z){
        v=x,w=y,Next=z;
    }
}E[MAXN*MAXN+5];
int ecnt,Adj[MAXN*2+5];
inline void AddEdge(int u,int v,double w){
    E[ecnt]=Edge(v,w,Adj[u]),Adj[u]=ecnt++;
    E[ecnt]=Edge(u,0,Adj[v]),Adj[v]=ecnt++;
}

int Cur[MAXN*2+5];
int Dist[MAXN*2+5];
bool bfs(int S,int T){
    for(int i=0;i<=N+M+1;i++)
        Cur[i]=Adj[i],Dist[i]=INF;
    queue<int> Q;
    Dist[S]=0,Q.push(S);
    while(!Q.empty()){
        int u=Q.front();Q.pop();
        for(int i=Adj[u];i!=-1;i=E[i].Next){
            int v=E[i].v;
            if(abs(E[i].w)>eps&&Dist[v]>Dist[u]+1){
                Dist[v]=Dist[u]+1;
                Q.push(v);
            }
        }
    }
    return Dist[T]<INF;
}
double dfs(int u,int T,double Min){
    if(abs(Min)<=eps||u==T)
        return Min;
    double Now=0,Rest=Min;
    for(int i=Cur[u];i!=-1;i=E[i].Next){
        Cur[u]=i;
        int v=E[i].v;
        if(Dist[v]==Dist[u]+1){
            if(abs(Now=dfs(v,T,min(Rest,E[i].w)))>eps){
                Rest-=Now;
                E[i].w-=Now;
                E[i^1].w+=Now;
                if(abs(Rest)<eps)
                    break;
            }
        }
    }
    return Min-Rest;
}
double Dinic(int S,int T){
    double MaxFlow=0;
    while(bfs(S,T))
        MaxFlow+=dfs(S,T,INF);
    return MaxFlow;
}

int Sum;
bool Check(double expt){
	//暴力重建
    ecnt=0;
    memset(Adj,-1,sizeof Adj);
    for(int i=1;i<=M;i++)
        for(int j=1;j<=N;j++)
            if(P[i][j])
                AddEdge(i,j+M,INF);
    for(int i=1;i<=M;i++)
        AddEdge(0,i,expt*B[i]);
    for(int i=1;i<=N;i++)
        AddEdge(i+M,N+M+1,A[i]);
    return abs(Dinic(0,N+M+1)-Sum)<=eps;
}

int main(){
    N=read(),M=read();
    for(int i=1;i<=N;i++)
        Sum+=(A[i]=read());
    for(int i=1;i<=M;i++)
        B[i]=read();
    for(int i=1;i<=M;i++)
        for(int j=1;j<=N;j++)
            P[i][j]=read();
    double Left=0,Right=INF;
    for(int i=1;i<=100;i++){
        double Mid=(Left+Right)/2;
        if(Check(Mid))
            Right=Mid;
        else
            Left=Mid;
    }
    printf("%f",Right);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/C20190102/article/details/100040956