P3324 [SDOI2015] 星际战争

P3324 [SDOI2015] 星际战争

大意

你有 \(m\) 个武器和 \(n\) 个目标,每个武器只能用来攻击相匹配的目标,最终问你最少需要多长时间能把这 \(n\) 个目标全部击毙。

思路

首先,我们考虑一个问题,这个题是与时间相关的,我们无法得知如何使得直接算这个时间。

我们不妨先想想这个 \(t\) 的事,对于当前一个成立的 \(t\),对于 \(T \ge t\),那么所有的 \(T\) 都成立,说人话就是,\(T\) 有单调性,那么我们就可以直接二分答案了。

但是目前的问题在于,我们如何判断这个时间 \(t\) 是否成立,我们想想,每个机器 \(i\) 能输出的最大伤害应当是 \(t \times b_i\),那么我们是否可以直接建上边,然后跑最大流,看看流满之后的答案是否是 \(\sum a_i\),如果是的话,那么我们可以试试 \(T < t\) 的值。

具体来说,就是在源点和武器间建容量为 \(t \times b_i\) 的边,在目标和汇点间建立容量为 \(a_i\) 的边,因为每个目标只需要 \(a_i\),最终,将每个武器和目标连容量为 \(\infty\) 的边,那么直接每次二分判断答案。

时间复杂度大概是 $$\mathcal{O}(100 \cdot (n+m) \cdot (nm)^2)$$。这里看自己写法,我直接固定了 \(100\) 次二分。

代码

#include<iostream>
#include<queue>
#include<cstring>
#include<cstdio>
using namespace std;

#define ciallo cerr << "Ciallo\n"
const int MAXN = 205;
int n, m;
double ans = 0.0;
int a[MAXN];
int b[MAXN];
bool f[MAXN][MAXN];
const double eps = 1e-5;

struct node{
    int u, v, nxt;
    double c;
}e[MAXN * 100];
int tot = 0, h[MAXN], pre[MAXN];
int S, T;

void add(int u, int v, double c){
    e[tot] = {u, v, h[u], c};
    h[u] = tot ++;
    e[tot] = {v, u, h[v], 0.0};
    h[v] = tot ++;
}

bool bfs(){
    memset(pre, -1, sizeof(pre));
    queue<int> q;
    q.push(S); pre[S] = -2;
    while(!q.empty()){
        int u = q.front(); q.pop();
        for(int i = h[u]; ~i; i = e[i].nxt){
            int v = e[i].v;
            if(pre[v] == -1 && e[i].c > eps){
                pre[v] = i;
                if(v == T){
                    return true;
                }
                q.push(v);
            }
        }
    }
    return false;
}

double EK(){
    double res = 0;
    while(bfs()){
//        ciallo;
        double Min = 1e18;
        for(int i = T; i != S; i = e[pre[i]].u){
            Min = min(Min, e[pre[i]].c);
        }
        for(int i = T; i != S; i = e[pre[i]].u){
            e[pre[i]].c -= Min;
            e[pre[i] ^ 1].c += Min;
        }
        res += Min;
    }
    return res;
}

bool check(double x){
    tot = 0;
    memset(h, -1, sizeof(h));
    for(int i = 1; i <= m; i ++){
        add(S, i, x * b[i]);
    }
    for(int i = 1; i <= n; i ++){
        add(i + m, T, a[i]);
    }
    for(int i = 1; i <= m; i ++){
        for(int j = 1; j <= n; j ++){
            if(f[i][j]){
                add(i, j + m, 1e9);
            }
        }
    }
    double cnt = EK();
    return (cnt + eps >= ans);
}

int main(){
    cin >> n >> m;
    S = 0, T = n + m + 1;
    for(int i = 1; i <= n; i ++){
        cin >> a[i];
        ans += a[i];
    }
    for(int i = 1; i <= m; i ++){
        cin >> b[i];
    }
    for(int i = 1; i <= m; i ++){
        for(int j = 1; j <= n; j ++){
            cin >> f[i][j];
        }
    }
    double l = 0.0, r = 1000000000.0;
    double ANS = 0;
    for(int i = 1; i <= 100; i ++){
        double mid = (l + r) / 2;
        if(check(mid)){
            ANS = mid;
            r = mid;
        }
        else{
            l = mid;
        }
    }
    printf("%.6lf\n", ANS);
    return 0;
}
posted @ 2026-06-11 13:10  To_Carpe_Diem  阅读(7)  评论(0)    收藏  举报