【SDOI2015第1轮第2试】星际战争

本文探讨了一个关于机器人摧毁的问题,并使用网络流算法解决。具体来说,通过将问题抽象为机关枪对机器人的攻击过程,利用二分查找与最大流算法确定摧毁所有机器人的最短时间。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目

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个巨型机器人。

对于30%的数据,1<=N, M<=5;
对于全部的数据,1<=N, M<=50,1<=Ai<=105,1<=Bi<=1000,输入数据保证X军团一定能摧毁Y军团的所有巨型机器人。

题意

就是有m个机关枪,n个机器,每个机关枪每秒可以造成ai点伤害。
每个机器也有bi点体力值,问多少秒后可以把全部的机器干掉。每个机关枪有指定的机器打。
秒数有可能为小数。

分析

我们先来分析题目要求什么?
其实就是要求每个机关枪打的最大值最小。
也就是二分。
现在我们考虑如何判定。
其实也很简单,我们把这个问题转换成网络流的问题。
从源点连到每个机关枪连容量为当前二分的mid*a[i]
然后每个机关枪向它们可以打的机器连maxlongint
机器再向汇点连bi。
最后只用判断最大流是否为bi就行了。

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxlongint 2147483647
using namespace std;
const int N=60,M=10000;
const double eps=0.00001;
int n,m,ai[N],bi[N],b[M],las[M],nex[M],nu,t,x,ma,d[M],a[M];
double mid,f[M],re[M],sum;
void insert(int x,int y,int z){
    b[++nu]=y;nex[nu]=las[x];las[x]=nu;f[nu]=z;re[nu]=z;
    b[++nu]=x;nex[nu]=las[y];las[y]=nu;f[nu]=0;
}
bool bfs(){
    int l=0,r=1;a[1]=0;
    memset(d,0,sizeof(d));d[0]=1;
    while (l<r){
        for(int p=las[a[++l]];p;p=nex[p]){
            if (!d[b[p]]&&f[p]>eps) {
                a[++r]=b[p];d[b[p]]=d[a[l]]+1;
            }
        }
    }
    return d[t];
}
double ditch(int x,double y){
    if (x==t) return y;
    double ll=0;
    for(int p=las[x];p;p=nex[p]){
        if (d[b[p]]==d[x]+1&&f[p]>eps){
            double u=ditch(b[p],min(y,f[p]));
            if (u>eps){
                y-=u;ll+=u;f[p]-=u;f[p^1]+=u;
                if (y<eps) break;
            }
        }
    }
    if (ll<eps) d[x]=-1;
    return ll;
}
bool check(double x){
    for(int i=1;i<=m;i++)re[i*2]=x*bi[i]*1.0,re[i*2+1]=0;
    double ans=0;
    for(int i=2;i<=nu;i++) f[i]=re[i]*1.0;
    while (bfs()) ans+=ditch(0,maxlongint);
    return sum-ans<eps;
}
int main(){
    scanf("%d%d",&n,&m);nu=1;t=n+m+1;
    ma=0,sum=0;
    for(int i=1;i<=n;i++){
        scanf("%d",&ai[i]);
        sum+=ai[i];
    }
    for(int i=1;i<=m;i++){
        scanf("%d",&bi[i]);
        insert(0,i,bi[i]);
        ma=max(ma,bi[i]);
    }
    for(int i=1;i<=n;i++)insert(i+m,t,ai[i]);
    for(int i=1;i<=m;i++)
       for(int j=1;j<=n;j++){
            scanf("%d",&x);
            if (x) insert(i,j+m,maxlongint);
       }
    double l=0,r=sum/ma;
    while(r-l>eps){
        mid=(l+r)/2*1.0;
        if (check(mid)) r=mid-eps;
        else l=mid+eps;
    }
    printf("%.6lf\n",l);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值