LightOJ1274(组合数)


题目好难懂~告诉你文章中有多少个单词,只有单词"Yes","No" , yes占三个字节,no是两个字节,共有 a个单词,s个字节

那么yes就是 n=s-2*a ,no就是m=(s-3*a)/2个

将yes看成1,no看成0

形成了01的字符串,每次将可组合成的某个字符串右移一位后,将最左端置位1,然后和原串每一位比较,得到不一样的位的个数,求的是所有的组合方式的平均值

数学公式推一推就出来的说~~~

理解为每一位不同的是多少次(一开始是用期望dp写的,开了三位数组,如果数据范围扩大,推组合数公式不会卡时间)

1有n个,0有m个,那么最高位的时候只有为0的才会不一样: C(n,n+m-1)

其他位置只要跟前面一个位置不一样就可以了,那么就是说当前和前面一个位置一个0一个1 , 共C(n-1 , n+m-2),因为1,0可以交换所以要乘2

之后 所有位置都是满足的:

平均值就是(2*(n+m-1)*C(n-1,n+m-2)+C(n,n+m-1)) / C(n,n+m)

----->(2*n*C(n,n+m-1)+C(n,n+m-1)) / C(n,n+m)

----->(2*n+1)*C(n,n+m-1) /(C(n,n+m-1)+C(n-1,n+m-1))

----->(2*n+1)*C(n,n+m-1) /((1+n/m)*C(n,n+m-1))

----->(2*n+1)/((1+n)/m)

----->(2*m*n+m)/(n+m)

先附期望dp的代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5010;
int n, s, m1, m2; 
double dp[2][N][2];

int main()
{
	int t, cas = 1;
	scanf("%d", &t);
	while(t--)
	{
		scanf("%d%d", &n, &s);
		m1 = s - n * 2;
		m2 = n * 3 - s;
		dp[n % 2][m1][0] = dp[n % 2][m1][1] = 0;
		for(int i = n - 1; i >= 0; i--)
		{
			for(int j = min(m1, i); j >= 0 && i - j <= m2; j--)
			{
				double p1 = (m1 - j) * 1.0 / (n - i);
				double p2 = (m2 - (i - j)) * 1.0 / (n - i);
				if(j + 1 <= m1)
				{
					dp[i % 2][j][0] = dp[(i + 1) % 2][j + 1][0] * p1 + (dp[(i + 1) % 2][j][1] + 1) * p2;
					dp[i % 2][j][1] = (dp[(i + 1) % 2][j + 1][0] + 1) * p1 + dp[(i + 1) % 2][j][1] * p2; 
				}
				else
				{
					dp[i % 2][j][0] = (dp[(i + 1) % 2][j][1] + 1) * p2; 
					dp[i % 2][j][1] = dp[(i + 1) % 2][j][1] * p2; 
				}
			}
		}
		printf("Case %d: %.12f\n", cas++, dp[0][0][0]);  
	}
	return 0;
}

然后是组合数的代码,是不是超短

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5010;
int n, s, m1, m2; 

int main()
{
	int t, cas = 1;
	scanf("%d", &t);
	while(t--)
	{
		scanf("%d%d", &n, &s);
		m1 = s - n * 2;
		m2 = n * 3 - s;
		double ans = (2.0 * m1 * m2 + m2) / (m1 + m2); 
		printf("Case %d: %.12f\n", cas++, ans);  
	}
	return 0;
}


  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值