'commit'
This commit is contained in:
35
TangDou/AcWing_TiGao/T5/QiWang/217.cpp
Normal file
35
TangDou/AcWing_TiGao/T5/QiWang/217.cpp
Normal file
@@ -0,0 +1,35 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
const int N = 1e5 + 10, M = N << 1;
|
||||
|
||||
int n, m;
|
||||
int h[N], e[M], ne[M], w[M], idx;
|
||||
int out[N];
|
||||
double f[N];
|
||||
|
||||
void add(int a, int b, int c) {
|
||||
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
|
||||
}
|
||||
double dfs(int u) {
|
||||
if (f[u] >= 0) return f[u]; // 如果u点计算过,记忆化搜索返回结果值
|
||||
f[u] = 0; // 初始化为0,准备开始填充
|
||||
for (int i = h[u]; ~i; i = ne[i]) {
|
||||
int j = e[i];
|
||||
f[u] += (w[i] + dfs(j)) / out[u];
|
||||
}
|
||||
return f[u];
|
||||
}
|
||||
int main() {
|
||||
memset(h, -1, sizeof h);
|
||||
cin >> n >> m;
|
||||
while (m--) {
|
||||
int a, b, c;
|
||||
cin >> a >> b >> c;
|
||||
add(a, b, c);
|
||||
out[a]++;
|
||||
}
|
||||
memset(f, -1, sizeof f);
|
||||
printf("%.2lf\n", dfs(1));
|
||||
return 0;
|
||||
}
|
||||
131
TangDou/AcWing_TiGao/T5/QiWang/217.md
Normal file
131
TangDou/AcWing_TiGao/T5/QiWang/217.md
Normal file
@@ -0,0 +1,131 @@
|
||||
##[$AcWing$ $217$. 绿豆蛙的归宿](https://www.acwing.com/problem/content/description/219/)
|
||||
|
||||
### 一、题目描述
|
||||
给出一个 **有向无环的连通图**,起点为 $1$ ,终点为 $N$,每条边都有一个长度。
|
||||
|
||||
数据保证从起点出发能够到达图中所有的点,图中所有的点也都能够到达终点。
|
||||
|
||||
绿豆蛙从起点出发,走向终点。
|
||||
|
||||
到达每一个顶点时,如果有 $K$ 条离开该点的道路,绿豆蛙可以选择任意一条道路离开该点,并且走向每条路的概率为 $1/K$。
|
||||
|
||||
现在绿豆蛙想知道,从起点走到终点所经过的路径总长度的 **期望** 是多少?
|
||||
|
||||
**输入格式**
|
||||
第一行: 两个整数 $N,M$,代表图中有 $N$ 个点、$M$ 条边。
|
||||
|
||||
第二行到第 $1+M$ 行: 每行 $3$ 个整数 $a,b,c$,代表从 $a$ 到 $b$ 有一条长度为 $c$ 的有向边。
|
||||
|
||||
**输出格式**
|
||||
输出从起点到终点路径总长度的 **期望值**,结果四舍五入保留两位小数。
|
||||
|
||||
**数据范围**
|
||||
$1≤N≤10^5,1≤M≤2N$
|
||||
|
||||
**输入样例:**
|
||||
```cpp {.line-numbers}
|
||||
4 4
|
||||
1 2 1
|
||||
1 3 2
|
||||
2 3 3
|
||||
3 4 4
|
||||
```
|
||||
|
||||
**输出样例:**
|
||||
```cpp {.line-numbers}
|
||||
7.00
|
||||
```
|
||||
|
||||
### 二、数学期望
|
||||
期望的起点一般都是唯一的,终点一般都是不唯一的。
|
||||
|
||||
所以,一般我们喜欢从终点倒推。
|
||||
|
||||
$f(i)$:从$i$跳到$N$的期望长度。边界$f(N)=0$
|
||||
答案:$f(1)$
|
||||
|
||||
|
||||
首先明白一点:到达某个结果的期望值 = 这个结果 * 从起始状态到这个状态的概率
|
||||
|
||||
**什么意思呢?**
|
||||
|
||||
如图:
|
||||
<center><img src='https://img-blog.csdnimg.cn/c8715153345a4f48b17bca9db4a18c45.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzUxOTY4MTU1,size_16,color_FFFFFF,t_70'></center>
|
||||
|
||||
我们计算从$1$号点到$3$号点的期望距离
|
||||
|
||||
路径$1$. $\displaystyle 1 \rightarrow 3:E_1=2×\frac{1}{2}=1$
|
||||
|
||||
路径$2$. $\displaystyle 1 \rightarrow 2 \rightarrow 3:E_2=1×\frac{1}{2}+3×\frac{1}{2}×1=2$
|
||||
|
||||
这里路径$2$中从$1$到$2$概率为$\displaystyle \frac{1}{2}$,但单看从$2$到$3$概率就是$1$,但是从$1$到$3$那就是从($1$到$2$的概率)$\displaystyle \frac{1}{2}$×$1$($2$到$3$的概率)=$\displaystyle \frac{1}{2}$。
|
||||
|
||||
所以从 点$1$ 到 点$3$ 的数学期望值=$1+2=3$
|
||||
><font color='red'><h5>总结
|
||||
① 概率是叠乘的
|
||||
② 概率计算出来后,需要乘上边权值,做为本边权的贡献值
|
||||
③ 所有贡献值累加和就是期望
|
||||
</h5></font>
|
||||
|
||||
|
||||
**有向无环图**
|
||||
|
||||

|
||||
|
||||
|
||||
题意:给定一个$DAG$,求路径和。
|
||||
|
||||
由题意知,题目中的$K$就是每个点的出度。
|
||||
|
||||
然后,又由题意知,最后一个点,它没有出度,而到自己的距离为$0$,即状态已经确定。
|
||||
|
||||
用前向星存图,从第一个点开始
|
||||
|
||||
$DFS$,每次枚举它所连的边,并把边的边权 ($w[i]$)加上,最后除以它的出度即可。
|
||||
|
||||
方程:
|
||||
$dp[n]=0 \\
|
||||
dp[i]+=(dp[son[i]]+e[son[i]].val)/out[i]$
|
||||
|
||||
其中,
|
||||
|
||||
$son[i]$是$i$所连得边,$e$是前向星,$val$是边权,$out[i]$是$i$的出度。
|
||||
|
||||
#### $Code$
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
|
||||
const int N = 1e5 + 10, M = N << 1;
|
||||
|
||||
int n, m;
|
||||
int h[N], e[M], ne[M], w[M], idx;
|
||||
int out[N];
|
||||
double f[N];
|
||||
|
||||
void add(int a, int b, int c) {
|
||||
e[idx] = b, w[idx] = c, ne[idx] = h[a], h[a] = idx++;
|
||||
}
|
||||
double dfs(int u) {
|
||||
if (f[u] >= 0) return f[u]; // 如果u点计算过,记忆化搜索返回结果值
|
||||
f[u] = 0; // 初始化为0,准备开始填充
|
||||
for (int i = h[u]; ~i; i = ne[i]) {
|
||||
int j = e[i];
|
||||
f[u] += (w[i] + dfs(j)) / out[u];
|
||||
}
|
||||
return f[u];
|
||||
}
|
||||
int main() {
|
||||
memset(h, -1, sizeof h);
|
||||
cin >> n >> m;
|
||||
while (m--) {
|
||||
int a, b, c;
|
||||
cin >> a >> b >> c;
|
||||
add(a, b, c);
|
||||
out[a]++;
|
||||
}
|
||||
memset(f, -1, sizeof f);
|
||||
printf("%.2lf\n", dfs(1));
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
64
TangDou/AcWing_TiGao/T5/QiWang/218.cpp
Normal file
64
TangDou/AcWing_TiGao/T5/QiWang/218.cpp
Normal file
@@ -0,0 +1,64 @@
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
const int N = 15;
|
||||
const int INF = 0x3f3f3f3f;
|
||||
double f[N][N][N][N][5][5];
|
||||
int A, B, C, D;
|
||||
|
||||
// 如果大小王翻出来放1里,则a++,放2里b++,...
|
||||
void add(int &a, int &b, int &c, int &d, int x) {
|
||||
if (x == 1) a++;
|
||||
if (x == 2) b++;
|
||||
if (x == 3) c++;
|
||||
if (x == 4) d++;
|
||||
}
|
||||
|
||||
/*
|
||||
功能:计算当前状态f(a,b,c,d,x,y)下的期望值
|
||||
*/
|
||||
double dfs(int a, int b, int c, int d, int x, int y) {
|
||||
// 记忆化搜索
|
||||
if (f[a][b][c][d][x][y] > 0) return f[a][b][c][d][x][y];
|
||||
|
||||
// 递归出口:当前状态是否到达目标状态,目标状态的期望值是0
|
||||
int ta = a, tb = b, tc = c, td = d; // 抄出来
|
||||
add(ta, tb, tc, td, x), add(ta, tb, tc, td, y); // 大王小王会改变四个花色的数量
|
||||
if (ta >= A && tb >= B && tc >= C && td >= D) return 0; // 如果条件全满足就是终止状态
|
||||
|
||||
// 当前状态下的剩余牌数量
|
||||
int rst = 54 - ta - tb - tc - td;
|
||||
if (rst <= 0) return INF; // 还没有完成目标,没有剩余的牌了,无解
|
||||
|
||||
// 当前状态可以向哪些状态转移
|
||||
double v = 1;
|
||||
if (a < 13) // 黑桃有剩余,可能选出的是黑桃
|
||||
v += dfs(a + 1, b, c, d, x, y) * (13 - a) / rst;
|
||||
if (b < 13) // 红桃有剩余,可能选出的是红桃
|
||||
v += dfs(a, b + 1, c, d, x, y) * (13 - b) / rst;
|
||||
if (c < 13) // 梅花有剩余,可能选出的是梅花
|
||||
v += dfs(a, b, c + 1, d, x, y) * (13 - c) / rst;
|
||||
if (d < 13) // 方块有剩余,可能选出的是方块
|
||||
v += dfs(a, b, c, d + 1, x, y) * (13 - d) / rst;
|
||||
|
||||
// 如果小王没有被选出
|
||||
if (x == 0)
|
||||
v += min(min(dfs(a, b, c, d, 1, y), dfs(a, b, c, d, 2, y)), min(dfs(a, b, c, d, 3, y), dfs(a, b, c, d, 4, y))) / rst;
|
||||
|
||||
// 如果大王没有被选出
|
||||
if (y == 0)
|
||||
v += min(min(dfs(a, b, c, d, x, 1), dfs(a, b, c, d, x, 2)), min(dfs(a, b, c, d, x, 3), dfs(a, b, c, d, x, 4))) / rst;
|
||||
|
||||
return f[a][b][c][d][x][y] = v;
|
||||
}
|
||||
|
||||
int main() {
|
||||
cin >> A >> B >> C >> D;
|
||||
|
||||
double res = dfs(0, 0, 0, 0, 0, 0); // 四种花色、大小王都还没有被抽取
|
||||
|
||||
if (res > INF / 2) // 因为是浮点数,不能用等号判断是不是相等,简单的办法就是INF/2
|
||||
puts("-1.000");
|
||||
else
|
||||
printf("%.3f\n", res);
|
||||
return 0;
|
||||
}
|
||||
156
TangDou/AcWing_TiGao/T5/QiWang/218.md
Normal file
156
TangDou/AcWing_TiGao/T5/QiWang/218.md
Normal file
@@ -0,0 +1,156 @@
|
||||
##[$AcWing$ $218$. 扑克牌 ](https://www.acwing.com/problem/content/description/220/)
|
||||
|
||||
### 一、题目描述
|
||||
$Admin$ 生日那天,$Rainbow$ 来找 $Admin$ 玩扑克牌。
|
||||
|
||||
玩着玩着 $Rainbow$ 觉得太没意思了,于是决定给 $Admin$ 一个考验。
|
||||
|
||||
$Rainbow$ 把一副扑克牌($54$张)随机洗开,倒扣着放成一摞。
|
||||
|
||||
然后 $Admin$ 从上往下依次翻开每张牌,每翻开一张黑桃、红桃、梅花或者方块,就把它放到对应花色的堆里去。
|
||||
|
||||
$Rainbow$ 想问问 $Admin$,得到 $A$ 张黑桃、$B$ 张红桃、$C$ 张梅花、$D$ 张方块需要翻开的牌的张数的期望值 $E$ 是多少?
|
||||
|
||||
特殊地,如果翻开的牌是大王或者小王,$Admin$ 将会把它作为某种花色的牌放入对应堆中,使得放入之后 $E$的值尽可能小。
|
||||
|
||||
> <font color='red' size=3><b> 注:从牌堆里面翻出来的牌的期望张数最少是多少? </b></font>
|
||||
> **期望**:用初等数学的语言去描述,就是平均值是多少。
|
||||
> $Q:$为什么会有最少的概念出现的呢?这个数量不是固定的吗?
|
||||
> 答:这是因为大王和小王可以被认为是其中四个花色中的某一种花色,这就导致产生了不同的事件。
|
||||
> 放到红桃里是一种事件,放到黑桃里是一种事件,...,这四个事件的期望不一定相同!
|
||||
> 选择就在大小王上,我们要选择一个期望张数最少的放法,问我们这个期望张数最小是多少。
|
||||
|
||||
###$yxc$经典语录
|
||||
> **图中,点是状态表示,边是状态转移**。
|
||||
> **理解**:前一个题中,需要建图,建图完成后才能用图进行状态表示和状态转移,本题,状态表示和状态转移都是很明确的,不需要建图。
|
||||
> **总结** :动态规划是精髓,建图与否是表象。
|
||||
|
||||
由于 $Admin$ 和 $Rainbow$ 还在玩扑克,所以这个程序就交给你来写了。
|
||||
|
||||
**输入格式**
|
||||
输入仅由一行,包含四个用空格隔开的整数,$A,B,C,D$。
|
||||
|
||||
**输出格式**
|
||||
输出需要翻开的牌数的期望值 $E$,四舍五入保留 $3$ 位小数。
|
||||
|
||||
如果不可能达到输入的状态,输出 `-1.000`。
|
||||
|
||||
**数据范围**
|
||||
$0≤A,B,C,D≤15$
|
||||
|
||||
**输入样例:**
|
||||
```cpp {.line-numbers}
|
||||
1 2 3 4
|
||||
```
|
||||
|
||||
**输出样例:**
|
||||
```cpp {.line-numbers}
|
||||
16.393
|
||||
```
|
||||
|
||||
### 二、题意分析
|
||||
|
||||
#### 状态表示
|
||||
$f[a][b][c][d][x][y]$ : 当前已翻开状态下,还需翻开牌的数量 **期望数**。
|
||||
|
||||
- $a,b,c,d$ 为已翻开的各类牌 (黑红花片) 的数量
|
||||
- $x,y$代表大、小王的状态($0$为未翻开,$1$代表已翻开且当做黑桃,以此类推), 设 $rst$ 为剩余牌的数量。
|
||||
$$rst=54-a-b-c-d-(x!=0)-(y!=0)$$
|
||||
|
||||
若 $a < 13$,则当前抽到黑桃的贡献为
|
||||
|
||||
$$\frac{13−a}{rst} \times f[a+1][b][c][d][x][y]$$
|
||||
|
||||
其余花色同理。若小王被抽取,取可转移状态期望最小的一个进行状态转移,其贡献为:
|
||||
|
||||
$$\frac{1}{rst} \times \min_{1≤i≤4}f[a][b][c][d][i][y]$$
|
||||
|
||||
大王同理。
|
||||
|
||||
记忆化搜索求解,若无牌可抽仍未到达 $a > = A \&\& b > = B \&\& c > = C \&\& d > = D$ 的终止状态,则期望为正无穷,代表不合法的状态。
|
||||
|
||||
|
||||
#### 三、实现代码
|
||||
|
||||
```cpp {.line-numbers}
|
||||
#include <bits/stdc++.h>
|
||||
using namespace std;
|
||||
const int N = 15;
|
||||
const int INF = 0x3f3f3f3f;
|
||||
double f[N][N][N][N][5][5];
|
||||
int A, B, C, D;
|
||||
|
||||
// 如果大小王翻出来放1里,则a++,放2里b++,...
|
||||
void add(int &a, int &b, int &c, int &d, int x) {
|
||||
if (x == 1) a++;
|
||||
if (x == 2) b++;
|
||||
if (x == 3) c++;
|
||||
if (x == 4) d++;
|
||||
}
|
||||
|
||||
/*
|
||||
功能:计算当前状态f(a,b,c,d,x,y)下的期望值
|
||||
*/
|
||||
double dfs(int a, int b, int c, int d, int x, int y) {
|
||||
// 记忆化搜索
|
||||
if (f[a][b][c][d][x][y] > 0) return f[a][b][c][d][x][y];
|
||||
|
||||
// 递归出口:当前状态是否到达目标状态,目标状态的期望值是0
|
||||
int ta = a, tb = b, tc = c, td = d; // 抄出来
|
||||
add(ta, tb, tc, td, x), add(ta, tb, tc, td, y); // 大王小王会改变四个花色的数量
|
||||
if (ta >= A && tb >= B && tc >= C && td >= D) return 0; // 如果条件全满足就是终止状态
|
||||
|
||||
// 当前状态下的剩余牌数量
|
||||
int rst = 54 - ta - tb - tc - td;
|
||||
if (rst <= 0) return INF; // 还没有完成目标,没有剩余的牌了,无解
|
||||
|
||||
// 当前状态可以向哪些状态转移
|
||||
double v = 1;
|
||||
if (a < 13) // 黑桃有剩余,可能选出的是黑桃
|
||||
v += dfs(a + 1, b, c, d, x, y) * (13 - a) / rst;
|
||||
if (b < 13) // 红桃有剩余,可能选出的是红桃
|
||||
v += dfs(a, b + 1, c, d, x, y) * (13 - b) / rst;
|
||||
if (c < 13) // 梅花有剩余,可能选出的是梅花
|
||||
v += dfs(a, b, c + 1, d, x, y) * (13 - c) / rst;
|
||||
if (d < 13) // 方块有剩余,可能选出的是方块
|
||||
v += dfs(a, b, c, d + 1, x, y) * (13 - d) / rst;
|
||||
|
||||
// 如果小王没有被选出
|
||||
if (x == 0)
|
||||
v += min(min(dfs(a, b, c, d, 1, y), dfs(a, b, c, d, 2, y)), min(dfs(a, b, c, d, 3, y), dfs(a, b, c, d, 4, y))) / rst;
|
||||
|
||||
// 如果大王没有被选出
|
||||
if (y == 0)
|
||||
v += min(min(dfs(a, b, c, d, x, 1), dfs(a, b, c, d, x, 2)), min(dfs(a, b, c, d, x, 3), dfs(a, b, c, d, x, 4))) / rst;
|
||||
|
||||
return f[a][b][c][d][x][y] = v;
|
||||
}
|
||||
|
||||
int main() {
|
||||
cin >> A >> B >> C >> D;
|
||||
|
||||
double res = dfs(0, 0, 0, 0, 0, 0); // 四种花色、大小王都还没有被抽取
|
||||
|
||||
if (res > INF / 2) // 因为是浮点数,不能用等号判断是不是相等,简单的办法就是INF/2
|
||||
puts("-1.000");
|
||||
else
|
||||
printf("%.3f\n", res);
|
||||
return 0;
|
||||
}
|
||||
```
|
||||
|
||||
#### $Q$:期望值为什么初始化为$1$?
|
||||
|
||||
<center><img src='https://img-blog.csdnimg.cn/4479c699431c421da4ab5d4f3f3db1bc.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzUxOTY4MTU1,size_16,color_FFFFFF,t_70'></center>
|
||||
|
||||
$f[v_i]$: 从$i$卡牌状态到终点状态所需要的**期望卡牌数**
|
||||
|
||||
每次抽一张牌变到下个状态,所以每条路径的权值为$1$
|
||||
|
||||
$$\large f[v]=p_1×(f[v_1]+1)+p_2×(f[v_2]+1)+p_3×(f[v_3]+1)+…+p_k×(f[v_k]+1) = \\
|
||||
\sum_{i=1}^{k}p_i+\sum_{i=1}^{k}p_i \times f[v_i]
|
||||
$$
|
||||
|
||||
因为$v$一定能到达下个局面,所以下个状态的概率和为$1$,这里的$\large \displaystyle \sum_{i=1}^{k}p_i=1$ 那么就有:$\displaystyle \large f[v]=1+\sum_{i=1}^{k}p_i \times f[v_i]$
|
||||
综上这里的$f[v]$可以初始化为$1$。
|
||||
|
||||
Reference in New Issue
Block a user