【NOIP2013模拟】导弹防御塔

Description

Freda的城堡——
“Freda,城堡外发现了一些入侵者!”
“喵…刚刚探究完了城堡建设的方案数,我要歇一会儿嘛lala~”
“可是入侵者已经接近城堡了呀!”
“别担心,rainbow,你看呢,这是我刚设计的导弹防御系统的说~”
“喂…别卖萌啊……”
Freda控制着N座可以发射导弹的防御塔。每座塔都有足够数量的导弹,但是每座塔每次只能发射一枚。在发射导弹时,导弹需要T1秒才能从防御塔中射出,而在发射导弹后,发射这枚导弹的防御塔需要T2分钟来冷却。
所有导弹都有相同的匀速飞行速度V,并且会沿着距离最短的路径去打击目标。计算防御塔到目标的距离Distance时,你只需要计算水平距离,而忽略导弹飞行的高度。导弹在空中飞行的时间就是 (Distance/V) 分钟,导弹到达目标后可以立即将它击毁。
现在,给出N座导弹防御塔的坐标,M个入侵者的坐标,T1、T2和V,你需要求出至少要多少分钟才能击退所有的入侵者。

Solution

讲解一下

首先,多个炮塔的导弹是可以同时发的,那么这道题就水很多了。
首先,考虑有发炮时间和冷却时间,对一个炮塔可以瞄准多个目标,多个目标瞄准后时间是连续的,所以可以接连不断地发炮。

用什么

发现是有两组点,长得那么像二分图啊。但是是求最小的分钟数,最大最小,二分!

怎么做

其实这是一个很经典的模型,一个图,求在一个过程之后的最大最小值,二分后,在判断是否可行。
因为可以发多个导弹,所以一个炮塔可以连出m条边,表示发m个炮。然后在连边的时候判断,这条边用的时间是否满足限制,否则不连边。

这道题好坑

输入的是n,m
但是下面的是以m,n的顺序输入的。
死白的是。

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
const int maxn=500000;
const int maxx=500000;
int i,j,t,n,m,ans,v,k;
double l,r,mid,dis[maxx],o,t1,t2;
int first[maxx],next[maxx],last[maxx],num,fan[maxn],d[maxx];
int x[maxn],y[maxn],xx[maxn],yy[maxn],tot;
int chang[maxx];
void add(int x,int y,double z){
    last[++num]=y;
    next[num]=first[x];
    first[x]=num;
    chang[num]=z;
    fan[num]=num+1;
    last[++num]=x;
    next[num]=first[y];
    first[y]=num;
    chang[num]=0;
    fan[num]=num-1;
}
double dian(int x,int y,int xx,int yy){
    return sqrt((x-xx)*(x-xx)+(y-yy)*(y-yy));
}
bool bfs(){
    int i,j,k;
    i=0;j=1;memset(dis,0,sizeof(dis));dis[0]=1;d[1]=0;
    while(i<j){
        k=first[d[++i]];
        while(k!=0){
            if(dis[last[k]]==0&&chang[k]>0){
                dis[last[k]]=dis[d[i]]+1;
                d[++j]=last[k];
            }
            k=next[k];
        }
    }
    return (dis[n*m+m+1]!=0);
}
int dinic(int x,int y){
    int i,j,t,p;
    if (x==n*m+m+1){
        return y;
    }
    j=first[x];
    p=0;
    while(j!=0){
        i=last[j];
        if (chang[j]>0&&dis[i]==dis[x]+1){
            t=dinic(i,min(y,chang[j]));
            if (t>0) {
                chang[j]-=t;
                chang[fan[j]]+=t;
                y-=t;p+=t;if (y==0) break;
            }

        }
        j=next[j];
    }
    if (p==0) dis[x]=-1;
    return p;
}
bool pan(int x){
    int t=0;
    ans=0;
    while(bfs()){
        t=dinic(0,0x7fffffff);
        ans+=t;
    } 
    if(ans==m)return 1;else return 0;
}
int main(){
    scanf("%d%d%lf%lf%d",&n,&m,&t1,&t2,&v);
    fo(i,1,m){
        scanf("%d%d",&x[i],&y[i]);
    }
    fo(i,1,n){
        scanf("%d%d",&xx[i],&yy[i]);
    }
    l=0,r=100000;
    while(r-l>1e-7){
        mid=(l+r)/2;
        memset(first,0,sizeof(first));
        num=0;
        fo(i,1,n) fo(j,1,m) add(0,(i-1)*m+j,1);
        fo(i,1,n){
            fo(j,1,m){
                fo(k,1,m){
                    o=dian(xx[i],yy[i],x[k],y[k])/v+t1*j/60+t2*(j-1);
                    if(o>mid)continue;
                    add((i-1)*m+j,n*m+k,1);
                }
            }
        }
        fo(i,1,m)add(n*m+i,n*m+m+1,1);
        if(pan(mid))r=mid;else l=mid;
    }
    printf("%.6lf\n",l);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值