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;
}
本文来自一名高中生,作者:To_Carpe_Diem

浙公网安备 33010602011771号