Sushi,(期望 dp,概率dp,线性dp )

Sushi - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

Problem Statement

There are N dishes, numbered ,2,…,N. Initially, for each i (1≤i≤N), Dish i has ai​ (1≤ai​≤3) pieces of sushi on it.

Taro will perform the following operation repeatedly until all the pieces of sushi are eaten:

  • Roll a die that shows the numbers 1,2,…,N with equal probabilities, and let i be the outcome. If there are some pieces of sushi on Dish i, eat one of them; if there is none, do nothing.

Find the expected number of times the operation is performed before all the pieces of sushi are eaten.

Constraints

  • All values in input are integers.
  • 1≤N≤300
  • 1≤ai​≤3

Input

Input is given from Standard Input in the following format:

N
a1​ a2​ ……aN​

Output

Print the expected number of times the operation is performed before all the pieces of sushi are eaten. The output is considered correct when the relative difference is not greater than 10−910−9.

Sample 1

InputcopyOutputcopy
3
1 1 1
5.5

The expected number of operations before the first piece of sushi is eaten, is 11. After that, the expected number of operations before the second sushi is eaten, is 1.51.5. After that, the expected number of operations before the third sushi is eaten, is 33. Thus, the expected total number of operations is 1+1.5+3=5.51+1.5+3=5.5.

Sample 2

InputcopyOutputcopy
1
3
3

Outputs such as 3.003.000000003 and 2.999999997 will also be accepted.

Sample 3

InputcopyOutputcopy
2
1 2
4.5

Sample 4

InputcopyOutputcopy
10
1 3 2 3 3 2 3 2 1 3
54.48064457488221

Sponsor

解析:

题解 AT4531 Sushi - 触情离殇 的博客 - 洛谷博客 (luogu.com.cn)

由于每个盘子最多只有3个寿司,所以我们可以以这个位突破口

状态划分:不重不漏,将状态转移所依据的状态体现出来;

f[i][j][k],表示:拿走1个寿司的盘子有 i 个,拿走2个寿司的盘子有 j 个,拿走3个寿司的盘子有 k 个的最大步数

状态转移:当前状态

有 (n-i-j-k)/n 的概率从选中空盘子,即从状态 f[i][j][k] 转移过来

有 i/n 的概率从选中有1个寿司的盘子,即从状态 f[i-1][j][k] 转移过来

有 j/n 的概率从选中有2个寿司的盘子,即从状态 f[i+1][j-1][k] 转移过来

有 k/n 的概率从选中有3个寿司的盘子,即从状态 f[i][j+1][k-1] 转移过来

所以状态转移方程:

f[i][j][k]=1+f[i][j][k]*(n-i-j-k)/n+f[i-1][j][k]*i/n+f[i+1][j-1][k]*j/n+f[i][j+1][k-1]*k/n;

化简得:
f[i][j][k]=n/(i+j+k)+f[i-1][j][k]*i/(i+j+k)+f[i+1][j-1][k]*j/(i+j+k)+f[i][j+1][k-1]*k/(i+j+k);

#include<iostream>
#include<string>
#include<cstring>
#include<cmath>
#include<ctime>
#include<algorithm>
#include<utility>
#include<stack>
#include<queue>
#include<vector>
#include<set>
#include<math.h>
#include<map>

using namespace std;
typedef long long LL;
const int N = 3e2 + 5;
int n;
int cnt[4];
double f[N][N][N];


int main() {
	cin >> n;
	for (int i = 1,a; i <= n; i++) {
		scanf("%d", &a);
		cnt[a]++;
	}
	//cout << cnt[1] << " " << cnt[2] << " " << cnt[3] << endl;
	for (int k = 0; k <= n; k++) {
		for (int j = 0; j <= n; j++) {
			for (int i = 0; i <= n; i++) {
				//cout << i << endl;
				if(i||j||k)
				f[i][j][k] += (double)n / (i + j + k);
				if (i)f[i][j][k] += (double)i / (i + j + k) * f[i - 1][j][k];
				if (j)f[i][j][k] += (double)j / (i + j + k) * f[i + 1][j - 1][k];
				if (k)f[i][j][k] += (double)k / (i + j + k) * f[i][j + 1][k - 1];
			}
		}
	}
	printf("%.10lf\n", f[cnt[1]][cnt[2]][cnt[3]]);
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值