首先我们考虑所有期望由什么情况组成。
1.单独用a类型的球。2.单独用b类型的球。3 a类型和b类型一起用.
然后dp的话很容易想出一个o(n^3)的算法。。。f[n][a][b]前n个怪兽我们用a 个a球,b个b球得到的期望。太慢~~~~~~~虽然能过2333333333333.
之所以慢的原因是分量太多,那能不能合一起些呢?
比如(单独用a和a,b一起用算一类) (单独用b算一类)意思就是,a,b一起用的贡献算在a头上。
然后方程式 fa[n][a][b]不过还是(n^3)的。还是慢。。。那么能把b的单独用的贡献算在a头上吗,嗯,如果可以方程就是f[n][a],哈哈n^2了,但是有个问题,你哪能保证一定有那么多个b呢?前面我们n^3的时候单独开了位计数b的个数,现在木有了,b很阔能超限啊!!!,怎么办呢?重点来了,这个方法很妙啊,我们首先将b分成两份,一份是k,一份是b-k,这样减少,单独用b的方案产生的贡献,算在a头上的贡献,b-k是算在a头上的,这样导致了这个算在a头上的贡献是可调的,导致用b的数目也是可调的,试想如果k=0那么就是所有b的贡献都算在了a头上,这样固然很好(比如每个b都产生很大的贡献然后每次都用B球),但是木有那么多b球给你更新啊!!!!所以我们要调k的值啦,然后每固定一个k值你阔以把所有b球的值看成是b-k a球不变,然后每个b球的贡献都算在a头上,在这种情况下求出来的最大实际值是 f[n][a]+用的b球数*k。每个k值对应一个情况,k=0的时候就是原始情况,虽然这种情况来说,每个b木有减少,整体相对于其他情况一定是最好的,求出来的实际值也一定是最好的,但是我前面说过了,木有那么多b啊!!所以要二分k,k越小对应的情况越好,但是,b不一定有那么多,所以看着调吧。
//由于这个不是我写的所以我感觉代码有点丑。。。但是能解释清楚
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N = 2010;
int n, a, b;
double p[N], u[N], q[N];
double f[N][N]; int g[N][N];
#define eps 1e-9
#define nowf (f[i][j])
#define nowg (g[i][j])
int dcmp(double x)
{
if (fabs(x)<eps)return 0;
return ((x>0) ? 1 : -1);
}
void up(double &f1, int &g1, double f2, int g2)
{
if (f2>f1 + eps){ f1 = f2; g1 = g2; }
}
int get(double K)
{
for (int i = 1; i <= n; ++i)
for (int j = 0; j <= a; ++j)
{
nowf = nowg = 0;
up(nowf, nowg, f[i - 1][j], g[i - 1][j]);//这里什么球都没用
up(nowf, nowg, f[i - 1][j] + u[i] - K, g[i - 1][j] + 1);//这里用的是b球因为(b-k)是从b球类分出来的所以用了(b-k)相当于用b
if (j)up(nowf, nowg, f[i - 1][j - 1] + p[i], g[i - 1][j - 1]);//用a球
if (j)up(nowf, nowg, f[i - 1][j - 1] + q[i] - K, g[i - 1][j - 1] + 1);//用a球和b球这里应该写成f+p[i]+(u[i]-k)+p[i]*u[i]就好理解
}
return g[n][a];
}
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", u + i);
for (int i = 1; i <= n; ++i)q[i] = p[i] + u[i] - p[i] * u[i];
double l = -1e4, mid, r = 1e4;
while (r - l>eps)
{
mid = (l + r)*0.5;//二分k值
if (get(mid)<b)r = mid;
else l = mid;
}
printf("%lf\n", f[n][a] + l*b);
return 0;
}
嗯,上面的算法还是太慢啦23333.。。1000ms+
既然阔以分b那么为什么不能分a呢,嗯分a后就能达到nlogn*logn了。。。快的飞起!!!!30ms+
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
using namespace std;
double u[3000], v[3000], p[3000];
double expect[3000];
int n, a, b;
int numa[3000], numb[3000];
void test(double aa, double bb)
{
for (int i = 1; i <= n; i++)
{
double d1 = u[i] - aa;
double d2 = v[i] - bb;
double d3 = p[i] - aa - bb;
double d4 = 0;
numa[i] = numa[i - 1];
numb[i] = numb[i - 1];
expect[i] = expect[i - 1];
if (d1 >= d2&&d1 >= d3&&d1 > d4)//比较顺序,和大于等于号是有讲究的。。
{ //尽量少用d3,d1和d2在相等时随便选
numa[i] += 1;
expect[i] += d1;
continue;
}
if (d2 >d1&&d2 >= d3&&d2 >d4)
{
numb[i]+= 1;
expect[i]+= d2;
continue;
}
if (d3 > d1&&d3 > d2&&d3 > d4)
{
numb[i] += 1;
numa[i] += 1;
expect[i] +=d3;
}
if (d4 >= d1&&d4 >= d3&&d4 >= d2)continue;
}
}
int main()
{
//double lllll = 0.000000000000000001;
//cout << lllll <<endl;
scanf("%d%d%d", &n, &a, &b);
for (int i = 1; i <= n; i++)
{
scanf("%lf", &u[i]);
}
for (int i = 1; i <= n; i++)
scanf("%lf", &v[i]);
for (int i = 1; i <= n; i++)
p[i] = u[i] + v[i] - u[i] * v[i];
double ll = 0, rr = 1;
double l, r;
while (rr - ll > 1e-12)
{
double mida = (ll + rr) / 2;
l = 0, r = 1;
while (r - l > 1e-12)
{
double midb = (l + r) / 2;
test(mida, midb);
if (numb[n] < b)
r = midb;
else
l = midb;
if (numb[n] == b)break;//同下
}
if (numa[n] < a)
rr = mida;
else
ll = mida;
if (numa[n] == a)break;//如果numa[n]==a的时候此时已经是极限了如果不break 然后ll=mida然后mida上升导致所得解偏小。
}
// cout << "ll:" << ll << " " << "l:" <<l << endl;
//cout << ll*numa[n] + l*numb[n] << endl;
// cout << numa[n] << " " << numb[n] << endl;
//cout << expect[n] << endl;
printf("%.5lf", expect[n] + numa[n]*ll + numb[n]*l);//注意这得写法不是 expect[n] + a*ll + b*l
}