XSY Dissertation6-贪心和DP专题——概率与期望dp 【BZOJ4318】【BZOJ3566】【BZOJ3029】

【BZOJ4318】OSU!

题意简述
osu 是一款群众喜闻乐见的休闲软件。
我们可以把osu的规则简化与改编成以下的样子:
一共有n次操作,每次操作只有成功与失败之分,成功对应1,失败对应0,n次操作对应为1个长度为n的01串。在这个串中连续的 X个1可以贡献X^3 的分数,这x个1不能被其他连续的1所包含(也就是极长的一串1,具体见样例解释)
现在给出n,以及每个操作的成功率,请你输出期望分数,输出四舍五入后保留1位小数。、
Input
第一行有一个正整数n,表示操作个数。接下去n行每行有一个[0,1]之间的实数,表示每个操作的成功率。
Output
只有一个实数,表示答案。答案四舍五入后保留1位小数。

Sample Input

3
0.5
0.5
0.5

Sample Output

6.0

HINT 样例说明
000分数为0,001分数为1,010分数为1,100分数为1,101分数为2,110分数为8,011分数为8,111分数为27,总和为48,期望为48/8=6.0
N<=100000
Latex什么的没有,能看就行,重在题解嘛对吧

题解
对于我们在处理当前的一个位置i,假设i-1的期望为\(a\),那么如果当前操作成功,能够加上

\[(a+1)^3-a^3=3a^2+3a+1 \]

这样子就把一个整体的东西拆成了一个一个加和。
注意的是期望的平方不等于平方的期望,要分开。

#include<cstdio>
 
using namespace std;
 
const int N=100010;
 
int n;
double x1[N], x2[N], f[N];
 
int main() {
    scanf("%d", &n);
    for(int i=1; i<=n; i++) {
        double ng;
        scanf("%lf", &ng);
        f[i]=f[i-1]+ng*(3*x2[i-1]+3*x1[i-1]+1);
        x1[i]=(x1[i-1]+1)*ng;
        x2[i]=(x2[i-1]+2*x1[i-1]+1)*ng;
    }
    printf("%.1lf", f[n]);
}

【BZOJ3566】【SHOI2014】概率充电器

经典老题了啊。题面就不给了。
我还傻乎乎的以为通电的期望可以直接加起来,然后发现一堆点的通电期望大于1。。。。
实际上由于通电的期望中有重叠,所以我们需要 容斥一下。但是由于对于一个点可能通电的情况一直在加,那么这样容斥就几乎不可能了。
所以我们为什么不考虑不通电的期望呢,不通电是且的关系,也就是上面的电不来电且这个点不通电且下面的点不来电,这样就可以乘起来了,不像通电,是或的关系。
既然乘起来那当然就好搞了。
注意两个式子。我们这里h[i]输入时表示一个点不通电的概率。
\(dfs1\)中我们处理他和他的儿子没有电通过来的期望。

\[h_i=h_i*h_{son}+h_i*(1-h_{son})*edge_i.c \]

\(dfs2\)中我们处理一个点不从上面来电的期望,也就是说对于一个点不向他儿子导电的期望,用数组\(h1\)表示。

\[设P=\frac{h_x*h1_x}{(h_y+(1-h_y)*edge_i.c} \]

也就是最后父亲点既不从祖父点,也不从自己和儿子来电的期望,除了当前在处理的儿子点我们爱管不管,因为对于儿子点我们只考虑父亲不来电,儿子向不向父亲来电不在我们讨论范围。

\[h1=P+(1-P)*(1-edge_i.c) \]

也很好理解。

#include<cstdio>

using namespace std;
 
const int N=500010;
 
struct EDGE {
    int y, nex;
    double c;
}edge[N<<1];
 
int n, head[N], cnt;
double h[N], h1[N];
void add(int x, int y, double c) {
    edge[++cnt].y=y;
    edge[cnt].c=c;
    edge[cnt].nex=head[x];
    head[x]=cnt;
}
 
void dfs1(int x, int fa) {
    for(int i=head[x]; i; i=edge[i].nex) {
        int y=edge[i].y;
        if(y==fa) continue;
        dfs1(y, x);
        h[x]*=(h[y]+(1-h[y])*edge[i].c);
    }
}
 
void dfs2(int x, int fa) {
	if(!fa) h1[x]=1;
    for(int i=head[x]; i; i=edge[i].nex) {
        int y=edge[i].y;
        if(y==fa) continue;
        double P=h[x]*h1[x]/(h[y]+(1-h[y])*edge[i].c);
        h1[y]=P+(1-P)*edge[i].c;
        dfs2(y, x);
    }
}
 
int main() {
    scanf("%d", &n);
    for(int i=1; i<n; i++) {
        int x, y; double c;
        scanf("%d%d%lf", &x, &y, &c);
        c=100-c;
        c/=100;
        add(x, y, c), add(y, x, c);
    }
    for(int i=1; i<=n; i++) scanf("%lf", &h[i]), h[i]=(100-h[i])/100;
    dfs1(1, 0);
    dfs2(1, 0);
    double ans=0;
    for(int i=1; i<=n; i++) ans+=1ll-h[i]*h1[i];
    printf("%.6lf", ans);
}

【CF167B】【BZOJ3029】守卫者的挑战

题意简述
打开了黑魔法师Van的大门,队员们在迷宫般的路上漫无目的地搜寻着关押平家boy的监狱的所在地。突然,眼前一道亮光闪过。“我,Billy,是黑魔法圣殿的守卫者。如果你能通过我的挑战,那么你可以带走黑魔法圣殿的地图……”瞬间,队员们被传送到了一个擂台上,最初身边有一个容量为K的包包。
擂台赛一共有N项挑战,各项挑战依次进行。第i项挑战有一个属性ai,如果ai>=0,表示这次挑战成功后可以再获得一个容量为ai的包包;如果ai=-1,则表示这次挑战成功后可以得到一个大小为1 的地图残片。地图残片必须装在包包里才能带出擂台,包包没有必要全部装满,但是队员们必须把 【获得的所有的】地图残片都带走(没有得到的不用考虑,只需要完成所有N项挑战后背包容量足够容纳地图残片即可),才能拼出完整的地图。并且他们至少要挑战成功L次才能离开擂台。
队员们一筹莫展之时,光明的守卫者Billy帮忙预估出了每项挑战成功的概率,其中第i项挑战成功的概率为pi%。现在,请你帮忙预测一下,队员们能够带上他们获得的地图残片离开擂台的概率。
输入
第一行三个整数N,L,K。
第二行N个实数,第i个实数pi表示第i项挑战成功的百分比。
第三行N个整数,第i个整数ai表示第i项挑战的属性值.
输出
一个整数,表示所求概率,四舍五入保留6 位小数。
样例

3 1 0
10 20 30
-1 -1 2
0.300000

HINT
若第三项挑战成功,如果前两场中某场胜利,队员们就有空间来容纳得到的地图残片,如果挑战失败,根本就没有获得地图残片,不用考虑是否能装下;若第三项挑战失败,如果前两场有胜利,没有包来装地图残片,如果前两场都失败,不满足至少挑战成功次()的要求。因此所求概率就是第三场挑战获胜的概率。
对于 100% 的数据,保证0<=K<=2000,0<=N<=200,-1<=ai<=1000,0<=L<=N,0<=pi<=100。

这数据范围不是肯定的暴力吗
直接上\(O(n^3)\)
\(f_{ijk}\)表示打到\(i\)局赢了\(j\)局获得容量为\(k\)的背包。
显然如果\(当前状态中k>n\)那就一定装得下, 那就干脆超过的全部用\(N\)代替吧。
\(XSY\)贼不要脸改了数据范围把\(k\)\(200\)改到\(2000\)还改了空间逼我用滚动数组

#include<bits/stdc++.h>

using namespace std;

const int N=200;

int n, m, k;
double f[2][N+10][(N<<1)+10], ans;
int a[N+10], b[N+10];

int main() {
	scanf("%d%d%d", &n, &m, &k);
	for(int i=1; i<=n; i++) scanf("%d", &a[i]);
	for(int i=1; i<=n; i++) scanf("%d", &b[i]);
	if(k+N>2*N) f[0][0][2*N]=1;
	else f[0][0][k+N]=1;
	int ii=1; 
	for(int i=1; i<=n; i++, ii^=1) {
		memset(f[ii], 0, sizeof(f[ii]));
		for(int j=0; j<i; j++) {
			for(int k=0; k<=2*N; k++) {
				if(b[i]==-1) {
					if(k>0)
						f[ii][j+1][k-1]+=f[ii^1][j][k]*0.01*a[i];
				} else {
					if(k+b[i]>2*N)
                        f[ii][j+1][2*N]+=f[ii^1][j][k]*0.01*a[i];
                    else
                        f[ii][j+1][k+b[i]]+=f[ii^1][j][k]*0.01*a[i];
				}
				f[ii][j][k]+=f[ii^1][j][k]*(1-0.01*a[i]);
			}
		}
	}
	ii^=1;
	for(int i=m; i<=n; i++) {
		for(int j=N; j<=N<<1; j++) {
			ans+=f[ii][i][j];
		}
	}
	printf("%.6f", ans);
}
posted @ 2021-04-12 11:32  2017gdgzoi1164  阅读(108)  评论(0)    收藏  举报