BZOJ 3993: [SDOI2015]星际战争

Description

 3333年,在银河系的某星球上,X军团和Y军团正在激烈地作战。在战斗的某一阶段,Y军团一共派遣了N个巨型机器人进攻X军团的阵地,其中第i个巨型机器人的装甲值为Ai。当一个巨型机器人的装甲值减少到0或者以下时,这个巨型机器人就被摧毁了。X军团有M个激光武器,其中第i个激光武器每秒可以削减一个巨型机器人Bi的装甲值。激光武器的攻击是连续的。这种激光武器非常奇怪,一个激光武器只能攻击一些特定的敌人。Y军团看到自己的巨型机器人被X军团一个一个消灭,他们急需下达更多的指令。为了这个目标,Y军团需要知道X军团最少需要用多长时间才能将Y军团的所有巨型机器人摧毁。但是他们不会计算这个问题,因此向你求助。

Input

第一行,两个整数,N、M。

第二行,N个整数,A1、A2…AN。

第三行,M个整数,B1、B2…BM。

接下来的M行,每行N个整数,这些整数均为0或者1。这部分中的第i行的第j个整数为0表示第i个激光武器不可以攻击第j个巨型机器人,为1表示第i个激光武器可以攻击第j个巨型机器人。

Output

 一行,一个实数,表示X军团要摧毁Y军团的所有巨型机器人最少需要的时间。输出结果与标准答案的绝对误差不超过10-3即视为正确。

Sample Input

2 2

3 10

4 6

0 1

1 1

Sample Output

1.300000

HINT

 【样例说明1】

战斗开始后的前0.5秒,激光武器1攻击2号巨型机器人,激光武器2攻击1号巨型机器人。1号巨型机器人被完全摧毁,2号巨型机器人还剩余8的装甲值;

接下来的0.8秒,激光武器1、2同时攻击2号巨型机器人。2号巨型机器人被完全摧毁。

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

Solution

去年我省省选某题。

当时都不会网络流的我实力垫底。

这道题的灵感来源于最近的一场CodeForces的D题,我博客中有过说明:。

大体思路就是二分答案mid,check过程中:

S连到M个激光武器,B[i]*mid的流量,

N个机甲连接到T,A[i]的流量,

然后根据能否攻击对于两个点i,j建立一条流量为INF的边。

跑一遍网络流看看最大流和Sum(A)大小即可。

注意精度要求,我已开始交的时候WA了,然后我们可以在程序中加入

1
2
3
4
5
const double eps = 0.000001;
double cmp(double a, double b) {
	if (-eps <= a - b && a-b <= eps) return true;
	return false;
}

比较浮点大小的时候直接调用该函数即可。

第一次交嫌32ms慢点,重新交了一遍…

Code

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#include <iostream>
#include <cstdio>
using namespace std;
const int INF = 1<<30;
typedef long long LL;
int getint() {
	int r = 0, k = 1; char c = getchar();
	for (; '0' > c || c > '9'; c = getchar()) if (c == '-') k = -1;
	for (; '0' <= c && c <= '9'; c = getchar()) r = r * 10 - '0' + c;
	return r * k;
}
int n, m, S, T;
const double eps = 0.000001;
double cmp(double a, double b) {
	if (-eps <= a - b && a-b <= eps) return true;
	return false;
}
const int maxedge = 90005;
const int maxnode = 250;
struct MaxFlowSolver {
	int S, T;
	struct edge_type {
		int to, next;
		double r;
	} edge[maxedge];
	int h[maxnode], cur[maxnode], dis[maxnode], cnte, L, R;
	void init(int l, int r) {
		cnte = 1;
		L = l;
		R = r;
		for (int i = L; i <= R; ++i) h[i] = 0;
		S = T = 0;
	}
	void ins(int u, int v, double w) {
		edge[++cnte].to = v;
		edge[cnte].next = h[u];
		edge[cnte].r = w;
		h[u] = cnte;
		edge[++cnte].to = u;
		edge[cnte].next = h[v];
		edge[cnte].r = 0;
		h[v] = cnte;
	}
	int q[maxnode];
	bool BFS() {
		int head = 0, tail = 1;
		q[0] = S;
		for (int i = L; i <= R; ++i) dis[i] = -1;
		dis[S] = 0;
		int now;
		while (head < tail) {
			now = q[head++];
			for (int i = h[now]; i; i = edge[i].next) {
				if (dis[edge[i].to] == -1 && !cmp(edge[i].r, 0.0)) {
					dis[edge[i].to] = dis[now] + 1;
					q[tail++] = edge[i].to;
				}
			}
		}
		return dis[T] != -1;
	}
	double DFS(int now, double a) {
		if (now == T || cmp(a, 0.0)) return a;
		double f;
		double ret = 0;
		for (int &i = cur[now]; i; i = edge[i].next) {
			if (dis[edge[i].to] != dis[now] + 1) continue;
			if ((f = DFS(edge[i].to, min(a, edge[i].r))) > 0) {
				a -= f;
				ret += f;
				edge[i].r -= f;
				edge[i^1].r += f;
				if (cmp(a, 0.0)) break;
			}
		}
		if (cmp(ret, 0.0)) dis[now] = -1;
		return ret;
	}
	double Dinic(int start, int end) {
		S = start; T = end;
		double ret = 0;
		while (BFS()) {
			for (int i = L; i <= R; ++i) cur[i] = h[i];
			ret += DFS(S, INF);
		}
		return ret;
	}
} G;
int A[60];
double B[60];
double suma = 0;
int can[60][60];
bool check(double x) {
	G.init(S, T);
	for (int i = 1; i <= m; ++i) G.ins(S, i+1, x*B[i]);
	for (int i =1; i <= m; ++i)
		for (int j = 1; j <= n; ++j)
			if (can[i][j])
				G.ins(i+1, j+1+m, INF);
	for (int i = 1; i <= n; ++i) G.ins(i+1+m, T, A[i]);
	double f = G.Dinic(S, T);
	return cmp(f, suma);
}
int main() {
	n = getint();
	m = getint();
	S = 1; T = n+m+2;
	for (int i =1; i <= n; ++i) { A[i] = getint(); suma += A[i]; }
	for (int i =1; i <= m; ++i) B[i] = getint();
	for (int i =1; i <= m; ++i)
		for (int j = 1; j <= n; ++j)
			can[i][j] = getint();
	double l = 1.0/50000, r = 500000, mid;
	for (int i = 0; i < 35; ++i) {
		mid = (l + r) / 2;
		if (check(mid)) r = mid;
		else l = mid;
	}
	printf("%.8lf", r);
	return 0;
}