「题目描述」
3333 年,在银河系的某星球上,X 军团和 Y 军团正在激烈地作战。
在战斗的某一阶段,Y 军团一共派遣了 $N$ 个巨型机器人进攻 $X$ 军 团的阵地。
其中第 $i$ 个巨型机器人的装甲值为$A_i$ ,当一个巨型机器人的装甲值减少到 0 或者以下时,这个巨型机器人就被摧毁了。
X 军团有 $M$ 个激光武器,其中第 $i$ 个激光武器每秒可以削减一个巨型机器人 $B_i$ 的装甲值。
激光武器的攻击是连续的。
这种激光武器非常奇怪,一个激光武器只能攻击一些特定的敌人。
Y 军团看到自己的巨型机器人被 X 军团一个一个消灭,他们急需下达更多的指令。
为了这个目标,Y 军团需要知道 X 军团最少需要用多长时间才能将 Y 军团的所有巨型机器人摧毁。
但是他们不会计算这个问题,因此向你求助。
「题目链接」
BZOJ 3993 星际战争 「SDOI 2015」
「解题思路」
答案显然可以二分,考虑如何判定:对于一个给定的时间,X 的武器能否消灭 Y 所有的机器人。
这种 供应-需求
式问题很适合网络流建图。
从 $S$ 向武器连边,容量为在给定时间内可以供应的伤害,即攻击力乘上时间。
机器人向 $T$ 连边,容量为将其摧毁所需的伤害,即装甲值。
根据攻击关系在 $S$ 和 $T$ 之间连边,容量为 $\infty$ 。
计算最大流,如果等于装甲值总和,则可行。否则不可行。
「AC 代码」
#include <cstdio>
#include <new>
#include <cfloat>
#include <algorithm>
#include <queue>
#include <numeric>
#include <cmath>
const double EPS = 1e-6;
inline int dcmp(double x){
if(fabs(x) <= EPS) return 0; else return x < 0 ? -1 : 1;
}
inline bool equal(double x, double y){
return !dcmp(x - y);
}
const int MAX_N = 50;
const int MAX_M = 50;
const int MAX_NODE = MAX_N + MAX_M + 2;
const int MAX_EDGE = 2 * (MAX_N + MAX_N * MAX_M + MAX_M);
struct Node{
struct Edge *e, *cur;
int d;
} nodes[MAX_NODE];
inline Node* node(int i){
return &nodes[i];
}
struct Edge{
Node *to;
double cap, res;
Edge *next, *rev;
Edge() {}
Edge(Node *fr, Node *to, double cap) : to(to), cap(cap), res(cap), next(fr->e) {}
} edges[MAX_EDGE], *ptr = edges;
inline void addEdge(int a, int b, double c){
Node *u = node(a), *v = node(b);
u->e = new (ptr++) Edge(u, v, c);
v->e = new (ptr++) Edge(v, u, 0);
u->e->rev = v->e, v->e->rev = u->e;
}
namespace Dinic{
int n;
Node *s, *t;
inline bool bfs(){
for(Node *v = node(0); v != node(n); v++) v->cur = v->e, v->d = 0;
std::queue<Node*> Q;
Q.push(s), s->d = 1;
while(!Q.empty()){
Node *v = Q.front(); Q.pop();
for(Edge *e = v->e; e; e = e->next){
if(dcmp(e->res) > 0 && !e->to->d){
e->to->d = v->d + 1;
if(e->to == t) return true; else Q.push(e->to);
}
}
}
return false;
}
inline double dfs(Node *v, double minRes = DBL_MAX){
if(v == t || !dcmp(minRes)) return minRes;
double sum = 0;
for(Edge *&e = v->cur; e; e = e->next){
if(e->to->d == v->d + 1){
double flow = dfs(e->to, std::min(minRes, e->res));
if(dcmp(flow) > 0){
sum += flow, minRes -= flow;
e->res -= flow, e->rev->res += flow;
if(!dcmp(minRes)) break;
}
}
}
return sum;
}
inline double maxFlow(int s, int t, int n){
Dinic::s = node(s), Dinic::t = node(t), Dinic::n = n;
for(Node *v = node(0); v != node(n); v++) for(Edge *e = v->e; e; e = e->next) e->res = e->cap;
double ans = 0;
while(bfs()) ans += dfs(Dinic::s);
return ans;
}
}
int n, m;
int a[MAX_N + 1], b[MAX_M + 1];
int s, t;
double sum;
inline int robot(int i){
return i;
}
inline int gun(int i){
return i + n;
}
inline bool check(double x){
for(int i = 1; i <= m; i++){
Edge *e = node(gun(i))->e->rev;
e->cap = x * b[i];
}
return equal(Dinic::maxFlow(s, t, n + m + 2), sum);
}
inline double solve(){
sum = std::accumulate(a + 1, a + n + 1, 0);
double min = *std::min_element(b + 1, b + m + 1);
double l = 0, r = sum / min;
for(int i = 0; i <= 30; i++){
double mid = (l + r) / 2;
if(check(mid)) r = mid; else l = mid;
}
return (l + r) / 2;
}
int main(){
scanf("%d%d", &n, &m);
s = 0, t = n + m + 1;
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
for(int i = 1; i <= m; i++) scanf("%d", &b[i]);
for(int i = 1; i <= m; i++) for(int j = 1; j <= n; j++){
int x;
scanf("%d", &x);
if(x) addEdge(gun(i), robot(j), DBL_MAX);
}
for(int i = 1; i <= n; i++) addEdge(robot(i), t, a[i]);
for(int i = 1; i <= m; i++) addEdge(s, gun(i), 0);
printf("%.6f\n", solve());
return 0;
}
就是这样啦~