CF739E Gosha is hunting(费用流/凸优化dp)
纪念合格考爆炸。
其实这个题之前就写过博客了,qwq但是不小心弄丢了,所以今天来补一下。
首先,一看到球的个数的限制,不难相当用网络流的流量来限制每个球使用的数量。
由于涉及到最大化期望,所以要使用最大费用最大流。
我们新建两个点\(ss,tt\),分别表示两种球。
那么我们现在考虑应该怎么计算期望呢。
首先,如果假设如果对于一个怪物用一个球,那么连边也就比较容易了
对于一个怪物\(x\)
我们\(ss -> x\),费用为\(p[i]\),流量为1。表示一个球在一个怪物上只能用一次。
\(tt\)也是同理。
然后对于每一个\(x->t\),费用是\(0\),流量是\(1\),表示一个怪物只能用一个球。
但是,要是每次不要求只能用一个球应该怎么做呢。
我们考虑,这条边的费用应该是多少。
两个球都用的期望应该是\(1-(1-p_i)(1-q_i)\)
经过展开,我们发现应该是\(p_i+q_i-p_i\times q_i\)
那么由于我们发现,由于用了两个球,所以已经获得了二者之和的收益,那么在这一侧,只需要在上述建图的基础上\(x->t\),费用是\(-p_i\times q_i\)即可。
最后跑一发最大费用最大流就能通过这个题qwq时间复杂度玄学。
#include<bits/stdc++.h>
#define pb push_back
#define mk make_pair
#define ll long long
#define db double
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 4010;
const int maxm = 3e6+1e2;
const double eps = 1e-10;
int point[maxn],nxt[maxm],to[maxm],pre[maxm],from[maxn];
double dis[maxn];
int vis[maxn];
double cost[maxm];
int flow[maxm];
double ans;
int n,m,cnt=1;
int s,t;
void addedge(int x,int y,db w,int f)
{
nxt[++cnt]=point[x];
pre[cnt]=x;
to[cnt]=y;
cost[cnt]=w;
flow[cnt]=f;
point[x]=cnt;
}
void insert(int x,int y,db w,int f)
{
addedge(x,y,w,f);
addedge(y,x,-w,0);
}
queue<int> q;
bool spfa(int s)
{
for (int i=1;i<=maxn-3;i++) dis[i]=-1e9;
memset(vis,0,sizeof(vis));
q.push(s);
dis[s]=0;
while (!q.empty())
{
int x = q.front();
q.pop();
vis[x]=0;
for (int i=point[x];i;i=nxt[i])
{
int p = to[i];
if (dis[p]-(dis[x]+cost[i])<-eps && flow[i]>0)
{
from[p]=i;
dis[p]=dis[x]+cost[i];
if (!vis[p])
{
q.push(p);
vis[p]=1;
}
}
}
}
if (dis[t]==-1e9) return false;
return true;
}
void mcf()
{
double x = 1e9;
for (int i=from[t];i;i=from[pre[i]]) x=min(x,1.0*flow[i]);
for (int i=from[t];i;i=from[pre[i]])
{
flow[i]-=x;
flow[i^1]+=x;
ans+=x*cost[i];
}
}
void solve()
{
while (spfa(s)) mcf();
}
db a[maxn],b[maxn];
int ss,sss;
int aa,bb;
int main()
{
n=read(),aa=read(),bb=read();
s=maxn-10;
ss=s+1;
t=s+3;
sss=ss+1;
insert(s,ss,0,aa);
insert(s,sss,0,bb);
for (int i=1;i<=n;i++) scanf("%lf",&a[i]);
for (int i=1;i<=n;i++) scanf("%lf",&b[i]);
for (int i=1;i<=n;i++)
{
insert(ss,i,a[i],1);
insert(sss,i,b[i],1);
insert(i,t,0,1);
insert(i,t,-a[i]*b[i],1);
}
solve();
printf("%.4lf\n",ans);
return 0;
}
但是其实这个题的正解是凸优化\(dp\)
首先,先做一个最\(naive\)的想法。
我们令\(dp[i][j][k]\)表示前\(i\)个怪物,用了\(j\)一号球,用了\(k\)个二号球
那么转移也是显然的。
每次只需要讨论一下对于当前的怪物是用几个球,用哪个即可。
但是这样的复杂度是\(O(n^3)\)的。
显然没有办法通过。
考虑怎么优化。
由于题目中涉及到的正好用几个球,并且通过打表发现函数是凸的,那么我们就可以直接用凸优化来优化掉一维。
(其实是可以直接优化两个的,但是我太懒,所以没写。)
我们对于当前二分的值,表示每选一个二号球,就可以多得到\(mid\)的期望。不限制选的个数。
那么不难得到下面的这个转移式子。
dp[i][j]=dp[i-1][j];
dp[i][j]=max(dp[i][j],dp[i-1][j]+(ymh){bb[i],1});
if (j)
{
dp[i][j]=max(dp[i][j],dp[i-1][j-1]+(ymh){a[i],0});
dp[i][j]=max(dp[i][j],dp[i-1][j-1]+(ymh){both[i],1});
}
然后通过调整\(mid\),通过正好选到\(k\)个二号球。
最后求一个\(dp\)数组,然后记得把贡献减去就行。
时间复杂度\(n^2log\),非常优秀。
(其实是如果精度太小会\(WA\),精度太大会\(TLE\))
但是完全可以做到\(nlog^2\)的。
给代码。
#include<bits/stdc++.h>
#define pb push_back
#define mk make_pair
#define ll long long
#define db double
using namespace std;
inline int read()
{
int x=0,f=1;char ch=getchar();
while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
while (isdigit(ch)){x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return x*f;
}
const int maxn = 2010;
const db eps = 1e-6;
struct ymh{
db val;
int num;
ymh operator + (const ymh &b) const
{
return (ymh){val+b.val,num+b.num};
}
};
ymh dp[maxn][maxn];
db a[maxn],b[maxn];
int n;
db l=-4,r=4;
inline int dcmp(double x,double y)
{
return x-y<-eps ? -1 : (x-y>eps ? 1 : 0);
}
inline ymh max(ymh a,ymh b)
{
int now = dcmp(a.val,b.val);
if (now==0)
{
if (a.num<b.num) return a;
else return b;
}
else
{
if(now==-1) return b;
else return a;
}
}
int numa,numb;
db aa[maxn];
db bb[maxn];
db both[maxn];
bool check(db lim)
{
for (int i=1;i<=n;i++) aa[i]=a[i];
for (int i=1;i<=n;i++) bb[i]=b[i]+lim;
for (int i=1;i<=n;i++) both[i]=1.0-(1.0-a[i])*(1.0-b[i])+lim;
for (register int i=1;i<=n;++i)
{
for (register int j=0;j<=numa;++j)
{
dp[i][j]=dp[i-1][j];
dp[i][j]=max(dp[i][j],dp[i-1][j]+(ymh){bb[i],1});
if (j)
{
dp[i][j]=max(dp[i][j],dp[i-1][j-1]+(ymh){a[i],0});
dp[i][j]=max(dp[i][j],dp[i-1][j-1]+(ymh){both[i],1});
}
// if (dp[i][j].num>numb) return false;
}
}
return dp[n][numa].num<=numb;
}
int main()
{
n=read(),numa=read(),numb=read();
for (int i=1;i<=n;i++) scanf("%lf",&a[i]);
for (int i=1;i<=n;i++) scanf("%lf",&b[i]);
double ans=0;
while (r-l>=eps)
{
db mid = (l+r)/2;
// memset(dp,0,sizeof(dp));
if (check(mid)) l=mid,ans=mid;
else r=mid;
//printf("%.4lf %d\n",mid,dp[n][numa].num);
}
//cout<<1<<endl;
//printf("%.4lf\n",ans);
//memset(dp,0,sizeof(dp));
check(ans);
//printf("")
//printf("%.4lf %d\n",dp[n][numa].val,dp[n][numa].num);
printf("%.4lf",dp[n][numa].val-1.0*numb*ans);
return 0;
}
CF739E Gosha is hunting(费用流/凸优化dp)的更多相关文章
- CF739E Gosha is hunting(费用流,期望)
根据期望的线性性答案就是捕捉每一只精灵的概率之和. 捕捉一只精灵的方案如下: 1.使用一个\(A\)精灵球,贡献为\(A[i]\) 2.使用一个\(B\)精灵球,贡献为\(B[i]\) 3.使用一个\ ...
- HZOJ 赤(CF739E Gosha is hunting)
本来没有打算写题解的,时间有点紧.但是这个wqs二分看了好久才明白还是写点东西吧. 题解就直接粘dg的了: 赤(red) 本题来自codeforces 739E,加大了数据范围. 首先对一只猫不会扔两 ...
- hdu 2686 费用流 / 双线程DP
题意:给一个方阵,求从左上角出到右下角(并返回到起点),经过每个点一次不重复,求最大获益(走到某处获得改点数值),下来时每次只能向右或向下,反之向上或向左. 俩种解法: 1 费用流法:思路转化:从左 ...
- CF739E Gosha is hunting
法一: 匹配问题,网络流! 最大费用最大流,S到A,B流a/b费0,A,B到i流1费p[i]/u[i],同时选择再减p[i]*u[i]? 连二次!所以i到T流1费0流1费-p[i]*u[i] 最大流由 ...
- CF739E Gosha is hunting 【WQS二分 + 期望】
题目链接 CF739E 题解 抓住个数的期望即为概率之和 使用\(A\)的期望为\(p[i]\) 使用\(B\)的期望为\(u[i]\) 都使用的期望为\(p[i] + u[i] - u[i]p[i] ...
- CF739E Gosha is hunting DP+wqs二分
我是从其他博客里看到这题的,上面说做法是wqs二分套wqs二分?但是我好懒呀,只用了一个wqs二分,于是\(O(nlog^2n)\)→\(O(n^2logn)\) 首先我们有一个\(O(n^3)\)的 ...
- BZOJ2040[2009国家集训队]拯救Protoss的故乡——模拟费用流+线段树+树链剖分
题目描述 在星历2012年,星灵英雄Zeratul预测到他所在的Aiur行星在M天后会发生持续性暴雨灾害,尤其是他们的首都.而Zeratul作为星灵族的英雄,当然是要尽自己最大的努力帮助星灵族渡过这场 ...
- 【BZOJ5252】林克卡特树(动态规划,凸优化)
[BZOJ5252]林克卡特树(动态规划,凸优化) 题面 BZOJ(交不了) 洛谷 题解 这个东西显然是随着断开的越来越多,收益增长速度渐渐放慢. 所以可以凸优化. 考虑一个和\(k\)相关的\(dp ...
- 【CF739E】Gosha is hunting(动态规划,凸优化)
[CF739E]Gosha is hunting(动态规划,凸优化) 题面 洛谷 CF 题解 一个\(O(n^3)\)的\(dp\)很容易写出来. 我们设\(f[i][a][b]\)表示前\(i\)个 ...
随机推荐
- 三.Go微服务--令牌桶实现原理
1. 前言 在上一篇文章 Go微服务: 令牌桶 当中简单的介绍了令牌桶实现的原理,然后利用 /x/time/rate 这个库 10 行代码写了一个基于 ip 的 gin 限流中间件,那这个功能是怎么实 ...
- VS Code 1.60 发布!竟然可以自动检测编程语言了!
北京时间 2021 年 9 月 3 日凌晨,微软正式发布 2021 年 8 月版的 Visual Studio Code.希望您会喜欢此版本中的许多更新与改进,以下是其中的一些亮点: * 自动语言检测 ...
- opengl中标准矩形像素点手动网格化为三角形条带的实现
这里以一张矩形图片为例进行说明: 一张图片的像素点是孤立的,导入opengl中进行绘制出来,看起来没问题,但是当我们放大图片时候,显示的就是一个个孤立的点,而没有像看图软件放大图片那样看起来还是连续的 ...
- WEB漏洞——SQL
由于我的博客是学到渗透的时候才做的,没有关于WEB漏洞的笔记,现在发现WEB层面的漏洞有些不太熟悉了,边写一下笔记边复习一下,就从sql注入开始吧 话不多说先上大佬写的表[ctfhub]SQL注入 - ...
- Python 高级特性(3)- 列表生成式
range() 函数 日常工作中,range() 应该非常熟悉了,它可以生成一个迭代对象,然后可以使用 list() 将它转成一个 list # 判断是不是迭代对象 print(isinstance( ...
- IKE~多预共享密钥问题~解决方案
原文链接:Configuring more than one Main-Mode Pre-Shared Key (PSK) *dialup* IPSec phase1 可能需要梯子来翻过高墙.文章内容 ...
- autoCAD绘制简单三维立体图形
第一步: 首先绘制一个简单的(封闭的)二维图形: 第二步: 变换观察视角,比如修改为 变换后的视角: 第三步: 选中闭合图形边框,使用组合键"ctrl + shift + E",然 ...
- Tensorflow保存神经网络参数有妙招:Saver和Restore
摘要:这篇文章将讲解TensorFlow如何保存变量和神经网络参数,通过Saver保存神经网络,再通过Restore调用训练好的神经网络. 本文分享自华为云社区<[Python人工智能] 十一. ...
- VUE005. 在data中使用 / 改变data,或在data中调用method函数
使用三方UI库时经常会遇到在data中写入方法的场景,如Element-UI的级联选择器(动态加载part)需要在data中写入lazyLoad. 但后端总会给出意想不到的需求: 通过接口调取一串数据 ...
- 使用Redis Stream来做消息队列和在Asp.Net Core中的实现
写在前面 我一直以来使用redis的时候,很多低烈度需求(并发要求不是很高)需要用到消息队列的时候,在项目本身已经使用了Redis的情况下都想直接用Redis来做消息队列,而不想引入新的服务,kafk ...