CF739E Gosha is hunting [费用流 或 凸优化DP]

44 篇文章 0 订阅
19 篇文章 0 订阅

传送门

DP 做法

首先暴力 n ^ 3的DP应该没有问题, 但是空间和时间都压不下来, 于是有了凸优化

f[i][j][k] 表示第 i 位, 用了j个宝贝球, k个超级球

现在变成 f[i][j] 表示第 i 位, 用了j个宝贝球, 随便用多少个超级球的最优方案

如果我们选出来的超级球大于b个, 那么我们将每个超级球的值减小 k , 这样选出来的可能会少一些

我们二分这个k, 直到选出来的超级球恰好为 b

同理, 将 j 一维也优化掉, 复杂度 O(n log^2)

费用流做法

首先原点向 A 连流量为 a 的边, 表示最多 a 个宝贝球, 先 B 连流量为 b 的边

然后 a 先每个 i 连 流量为1, 费用为 p[i] 的边, b 向 i 连流量为1, 费用为v[i] 的边

然后每个点向终点连流量为 1 的边

等等, 两个都选呢 ? 此时到 i 的流量为2, 有1会通过费用为0的边到终点

我们把选两个的式子拆开, 发现需要减去 p[i] * v[i], 于是每个i再向原点连费用为 - p[i] * v[i], 流量为1 的边


#include<bits/stdc++.h>
#define N 2005
using namespace std;
const double eps = 1e-12;
int n, a, b;
double f[N], p[N], v[N]; int cnta[N], cntb[N];
void calc(double dx, double dy){
	for(int i=0; i<=n; i++) f[i] = cnta[i] = cntb[i] = 0;
	for(int i=1; i<=n; i++){
		f[i] = f[i-1]; cnta[i] = cnta[i-1]; cntb[i] = cntb[i-1];
		if(f[i-1] + p[i] > f[i] + dx) f[i] = f[i-1] + p[i] - dx, cnta[i] = cnta[i-1] + 1, cntb[i] = cntb[i-1];
		if(f[i-1] + v[i] > f[i] + dy) f[i] = f[i-1] + v[i] - dy, cnta[i] = cnta[i-1], cntb[i] = cntb[i-1] + 1;
		if(f[i-1] + (1 - (1 - p[i]) * (1 - v[i])) > f[i] + dx + dy) 
			f[i] = f[i-1] + (1 - (1 - p[i]) * (1 - v[i])) - dx - dy,
				cnta[i] = cnta[i-1] + 1, cntb[i] = cntb[i-1] + 1;
	}
}
int main(){
	scanf("%d%d%d", &n, &a, &b);
	for(int i=1; i<=n; i++) scanf("%lf", &p[i]);
	for(int i=1; i<=n; i++) scanf("%lf", &v[i]);
	double l = 0, r = 1, L, R;
	while(r - l > eps){
		double mid = (l+r) / 2;
		L = 0, R = 1;
		while(R - L > eps){
			double Mid = (L+R) / 2;
			calc(mid, Mid);
			if(cntb[n] > b) L = Mid; else R = Mid;
		}
		calc(mid, R); 
		if(cnta[n] > a) l = mid; else r = mid;
	} calc(r, R);
	printf("%.5lf", f[n] + a * r + b * R);
	return 0;
}

 

#include<bits/stdc++.h>
#define N 100050
using namespace std;
const double eps = 1e-10;
int first[N], nxt[N], to[N], w[N], tot = 1; double c[N];
void add(int x, int y, int z, double v){
	nxt[++tot] = first[x], first[x] = tot, to[tot] = y, w[tot] = z, c[tot] = v;
	nxt[++tot] = first[y], first[y] = tot, to[tot] = x, w[tot] = 0, c[tot] = -v;
} 
int n, a, b; double p[N], v[N];
int st, ed, A, B;
double dis[N]; bool vis[N];
int from[N], froms[N];
bool spfa(){
	for(int i=0; i<=n+3; i++) dis[i] = 1e9, vis[i] = 0;
	queue<int> q; q.push(st); vis[st] = 1; dis[st] = 0;
	while(!q.empty()){
		int x = q.front(); q.pop(); vis[x] = 0;
		for(int i=first[x];i;i=nxt[i]){
			int t = to[i];
			if(w[i] && dis[x] + c[i] + eps < dis[t]){
				dis[t] = dis[x] + c[i]; from[t] = x; froms[t] = i;
				if(!vis[t]) q.push(t), vis[t] = 1;
			}
		}
	} return dis[ed] < 1e9;
} 
int calc(){
	int u = ed, flow = 0x3fffffff;
	while(u){ flow = min(flow, w[froms[u]]); u = from[u];} u = ed; 
	while(u){ w[froms[u]] -= flow; w[froms[u] ^ 1] += flow; u = from[u];} 
	return flow;
}
double dinic(){ double ans = 0; while(spfa()) ans += dis[ed] * calc(); return ans;}
int main(){
	scanf("%d%d%d", &n, &a, &b);
	for(int i=1; i<=n; i++) scanf("%lf", &p[i]);
	for(int i=1; i<=n; i++) scanf("%lf", &v[i]);
	st = 0; ed = n + 1; A = n + 2; B = n + 3;
	add(st, A, a, 0); add(st, B, b, 0);
	for(int i=1; i<=n; i++){
		add(A, i, 1, -p[i]); add(B, i, 1, -v[i]); 
		add(i, ed, 1, 0); add(i, ed, 1, p[i] * v[i]);
	} printf("%.4lf", -dinic());
	return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

FSYo

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值