【赛前复习】概率期望

前言

不会不会,脑子残废。

我就是废物一个……

学习博客

讲的挺好的,虽然还是有一丢丢没懂(我就是个屑

数论小白都能看懂的数学期望讲解

还有推荐的博客吗,给个链接呗。

【疯狂眨眼】

简单介绍

概率就是概率,还能怎么解释?

期望嘛, 通俗的讲,就是……结果乘以其概率。(但是中间有很多需要注意的点)

题目练习

因为我菜,所以题目都很简单。。

大佬别看了,对没错,说的就是你!

快点最上面的那个 × 。

欸,快点啊,拜拜ヾ(•ω•`)o

Bovine Bones G

Bovine Bones G

我是真的没怎么理解这玩意儿怎么用期望搞,暴力循环不香嘛。

蹲个大佬讲解一下。

好的,下一个。

优惠券 Coupons

UVA10288 优惠券 Coupons

每张彩票上有一个漂亮图案,图案一共n种,如果你集齐了这n种图案就可以召唤神龙兑换大奖。

现在请问,在理想(平均)情况下,你买多少张彩票才能获得大奖的?
n \(\leq\) 33

设 已获得的种类数为 x,又拿了 a 次才拿到新的种类;

这样可以推出 概率, 继而搞到 期望。

反正推狮子推过去推过来的,就得到了一个很妙的狮子。

我懒,不打了,看挂的那篇博客吧。

以前做的,至今仍然记得输出极其恶心!


#include<cstdio>
#include<algorithm>
using namespace std;
#define int long long
const int N = 1e3 + 5;
int t,n,a[N],b[N],c[N];
int gcd(int a,int b) {
	return !b ? a : gcd(b,a % b);
}
int cnt(int x) {
	int res = 0;
	while(x) {
		res ++;
		x /= 10;
	}
	return res;
}
signed main() {
	while(~scanf("%lld",&n)) {
		a[n] = 0;
		b[n] = 1;
		c[n] = 0;
		for(int i = n - 1; i >= 0; i --) {
			c[i] = c[i + 1] + n / (n - i);
			int tmp = n % (n - i);
			int gcda = gcd(b[i + 1], n - i);
			b[i] = b[i + 1] / gcda * (n - i);
			a[i] = a[i + 1] * (n - i) / gcda + tmp * b[i + 1] / gcda;
			gcda = gcd(a[i],b[i]);
			a[i] /= gcda;
			b[i] /= gcda;
			c[i] += a[i] / b[i];
			a[i] %= b[i];
		}
		if(!a[0]) {
			printf("%lld\n",c[0]);
			continue;
		}
		int x = cnt(c[0]);
		for(int i = 1; i <= x + 1; i ++) printf(" ");
		printf("%lld\n",a[0]);
		printf("%lld ",c[0]);
		int y = cnt(max(a[0],b[0]));
		for(int i = 1; i <= y; i ++) printf("-");
		printf("\n");
		for(int i = 1; i <= x + 1; i ++) printf(" ");
		printf("%lld\n",b[0]);
	}
	return 0;
}

OSU!

OSU!

给定一个01串,在这个串中连续的 \(X\)\(1\)可以贡献\(X^3\) 的分数

给出n次操作的成功率 p[i],求期望分数。

DP

令 f[i] 表示 前 i 位总的期望, p[i] 为 第 i 位为 1 的概率。

顺着 DP 过来就好了,

关于 3 次方的处理,就多维护一个 2 次 和 1 次的数组a[]、b[],推一推狮子。

\[f[i] = (f[i - 1] + 3 × b[i − 1] + 3 × a[i − 1] + 1) × p[i] + f[i−1] × (1 − p[i]) \]

因为求的是总的期望嘛,所以还要加上 后面那一坨 当第 i 位没有取到 1 时的贡献。

狮子还挺好推的。

关于次方的处理就很模板化了。

代码也是以前打的,好像跟讲的不大一样,先不管了。

等我周末回家了补(老鸽子人了


#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 1e5 + 5;
int t,n;
double dp[N][4],a[N],ans;
int main() {
	scanf("%d",&n);
	for(int i = 1; i <= n; i ++) scanf("%lf",&a[i]);
	for(int i = 1; i <= n; i ++) {
		dp[i][1] = a[i] * (dp[i - 1][1] + 1);
		dp[i][2] = a[i] * (dp[i - 1][2] + 2 * dp[i - 1][1] + 1);
		dp[i][3] = a[i] * (dp[i - 1][3] + 3 * dp[i - 1][2] + 3 * dp[i - 1][1] + 1);
		ans += (1 - a[i + 1]) * dp[i][3];
	}
	printf("%.1lf\n",ans);
	return 0;
} 

收集邮票

收集邮票

n个数1~n,第k次取数需要k元,每次取数对于所有数概率均等(\(\frac{1}{n}\) ),问取完n个数的期望花费

怎么办,我不想写了。

那就不写了吧,


#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 1e4 + 5;
int n;
double p,ans[MAXN],sum[MAXN];
int main() {
	scanf("%d",&n);
	for(int i = 1; i <= n; i ++) {
		ans[i] = ans[i - 1] + 1.0 * n / (n - i + 1);
		sum[i] = sum[i - 1] + ans[i] * 1.0 * n / (n - i + 1);
	}
	printf("%.2lf",sum[n]);
	return 0;
}

跳过,下一个。

游走

游走

有一个 nn 个点, mm 条边的有向无环图,随机选择一条路径,求路径的期望长度。

这道题让我深刻明白了 期望其实不一定要把概率算出来

有些题目可以直接用 最终答案 / 总方案数

方案数和总长度的计算,就是简单的图上 点权 和 边权的统计。

所以啊,看我做题时候写的思路,我就是个屑!


/*

题目:B 城可以看作一个有 n个点 m条边的有向无环图。可能存在重边。
zbw 在 B城随机游走,他会随机选择一条路径,选择所有路径的概率相等。路径的起点和终点可以相同。
定义一条路径的长度为经过的边数,你需要求出 zbw 走的路径长度的期望,答案对 998244353取模。

思路:

遍历一遍图,类似于传递?但是我不知道怎么搞概率……

有向无环图啊……

建立源点和汇点(逐渐跑偏),加上边权, 然后开搞? 

概率??????算寂寞啊 

az, 期望不是只能通过概率算……,我是憨批。

可以最后处理。 

好的,我萎了。 

*/
#include<cstdio>
#include<algorithm>
using namespace std;
#define int long long
const int N = 1e5 + 5, M = 7e5 + 5, mod = 998244353;
int n, m, tot, head[N], to[M], nex[M], ans, cnt;
void add(int x, int y) {
	to[++tot] = y, nex[tot] = head[x], head[x] = tot;
}
int sum[N], siz[N]; 
void dfs(int x) {
	int ver; 
	if(siz[x]) return;
	siz[x] = 1;
	for(int i = head[x]; i; i = nex[i]) {
		ver = to[i];
		dfs(ver);
		siz[x] = (siz[x] + siz[ver]) % mod;
		sum[x] = (sum[x] + sum[ver] + siz[ver]) % mod;
	} 
}

int qpow(int a, int b) {
	int res = 1;
	while(b) {
		if(b & 1) res = res * a % mod;
		b >>= 1, a = a * a % mod;
	}
	return res;
} 

signed main() {
	scanf("%lld %lld", &n, &m);
	int u, v;
	for(int i = 1; i <= m; i ++) {
		scanf("%lld %lld", &u, &v);
		add(u, v);
	} 
	for(int i = 1; i <= n; i ++) {
		if(!siz[i]) dfs(i);
	}
	for(int i = 1; i <= n; i ++) {
		ans = (ans + sum[i]) % mod;
		cnt = (cnt + siz[i]) % mod;
	}
	ans = ans * qpow(cnt, mod - 2) % mod;
	printf("%lld", ans);
	return 0;
} 

换教室

换教室

你有 ≤m 次换课机会从班级c[i] 转换到班级 d[i],但是只有 k% 的概率能够成功转换,最终代价就是依次从第一个教室到最后一个教室的最短路径。问你期望总和最小是多少?

好题。妙

就是读题需要点耐心。虽然我看到长题面就很暴躁

dp 的状态定义挺常见的,

dp[i][j][0/1] 表示 前 i 节课 一共申请改变了 j 节, 第 i 节 申请/没申请 的期望代价

每次算贡献时,都要把前一个概率和当前概率带进去。因为是期望嘛。

体力值直接算。

然后就是喜闻乐见的推狮子了

就是要注意最短路的循环, k 是放在外面的。(不要问我是怎么知道的!)


/*

妙啊~

dp[i][j][0/1] 表示 前 i 节课 改变了 j 节, 第 i 节 改/没改 的期望

体力值直接算。 

然后就是喜闻乐见的推狮子了 


*/

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
const int N = 2005;
int n, m, v, e, c[N], d[N], dis[305][305];
double dp[N][N][2], ans = 1e9, K[N];
int main() {
	scanf("%d %d %d %d", &n, &m, &v, &e);
	for(int i = 1; i <= n; i ++) scanf("%d", &c[i]);
	for(int i = 1; i <= n; i ++) scanf("%d", &d[i]);
	for(int i = 1; i <= n; i ++) scanf("%lf", &K[i]);
	
	memset(dis, 0x3f, sizeof(dis));
	for(int i = 0; i <= n; i ++) {
		for(int j = 0; j <= m; j ++) dp[i][j][0] = dp[i][j][1] = 1e9;
	}
	
	for(int i = 1; i <= v; i ++) dis[i][i] = dis[0][i] = dis[i][0] = 0;
	int uu, vv, ww;
	while(e --) {
		scanf("%d %d %d", &uu, &vv, &ww);
		dis[uu][vv] = dis[vv][uu] = min(ww, dis[vv][uu]); 
	} 
	for(int k = 1; k <= v; k ++) {
		for(int i = 1; i <= v; i ++) {
			for(int j = 1; j <= v; j ++) {
				dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
			}
		}
	}
	dp[1][0][0] = dp[1][1][1] = 0;
	for(int i = 2; i <= n; i ++) {
		dp[i][0][0] = dp[i - 1][0][0] + dis[c[i - 1]][c[i]];
		for(int j = 1; j <= min(i, m); j ++) {
			dp[i][j][0] = min(dp[i][j][0], min(dp[i - 1][j][0] + dis[c[i - 1]][c[i]], dp[i - 1][j][1] + dis[d[i - 1]][c[i]] * K[i - 1] + dis[c[i - 1]][c[i]] * (1 - K[i - 1])));
			dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j - 1][0] + dis[c[i - 1]][c[i]] * (1 - K[i]) + dis[c[i - 1]][d[i]] * K[i]);
			dp[i][j][1] = min(dp[i][j][1], dp[i - 1][j - 1][1] + dis[d[i - 1]][c[i]] * K[i - 1] * (1 - K[i]) + dis[c[i - 1]][c[i]] * (1 - K[i - 1]) * (1 - K[i]) + dis[d[i - 1]][d[i]] * K[i - 1] * K[i] + dis[c[i - 1]][d[i]] * (1 - K[i - 1]) * K[i]);
		}
	}
	for(int i = 0; i <= m; i ++) ans = min(ans, min(dp[n][i][0], dp[n][i][1]));
	printf("%.2lf", ans);
	return 0;
}


配对

配对

笑死了,这道题目告诉我们,不要轻易相信洛谷的标签。

因为这道题——根!本!不!是!期!望!!!!1

注意如果你的极大值可能会爆 int ,所以开 long long 吧。


/*

我是真没看出来哪里期望了。

可能是我太菜了吧

 辣鸡标签,根本不是期望!!!!
 
 贪心 + dp就好了
 
 先从小到大排序,dp[i] 表示前 i 个匹配的最小绝对值
 
 dp[i] 可由 dp[i - 2] 和 dp[i - 3] 转移过来 


*/ 
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
#define int long long
const int N = 1e5 + 5;
int n, a[N], b[N], dp[N];
int num(int x, int y) {
	if(a[x] == b[y]) return 1e9;
	return a[x] < b[y] ? b[y] - a[x] : a[x] - b[y];
}
signed main() {
	scanf("%lld", &n);
	for(int i = 1; i <= n; i ++) scanf("%lld %lld", &a[i], &b[i]);
	if(n == 1 && a[1] == b[1]) {
		puts("-1");
		return 0;
	}
	sort(a + 1, a + 1 + n), sort(b + 1, b + 1 + n);
	dp[1] = num(1, 1);
	dp[2] = min(num(1, 1) + num(2, 2), num(1, 2) + num(2, 1));
	for(int i = 3; i <= n; i ++) {
		dp[i] = dp[i - 1] + num(i, i);
		dp[i] = min(dp[i], dp[i - 2] + num(i - 1, i) + num(i, i - 1));
		dp[i] = min(dp[i], dp[i - 3] + num(i - 2, i) + num(i - 1, i - 1) + num(i, i - 2));
		dp[i] = min(dp[i], dp[i - 3] + num(i - 2, i - 1) + num(i - 1, i) + num(i, i - 2));
		dp[i] = min(dp[i], dp[i - 3] + num(i - 2, i) + num(i - 1, i - 2) + num(i, i - 1));
	} 
	printf("%lld", dp[n]);
	return 0;
} 


posted @ 2021-10-14 20:13  Spring-Araki  阅读(54)  评论(0编辑  收藏  举报