Probability and Expectation DP

table of Contents

1. Probability DP

CSU 1123: PK martial arts leader

CSU 1342:Double

CSU 1725 Garrosh Hellscream vs. Ethereal Thief Rafham

Second, expect DP

LIghtOJ 1038 Race to 1 Again

HDU 4405 Aeroplane chess (flying chess)

POJ 2096 Collecting Bugs

SGU 495 Kids and Prizes


1. Probability DP

CSU 1123: PK martial arts leader

topic:

Description

Feng Zhiyu thought that he was very strong and wanted to be the leader of the martial arts, so he asked the current leader of the martial arts copper hydroxide to challenge. Copper hydroxide readily accepted the challenge, and the two agreed to have a decisive battle on the three pillars on the HDU campus on the night of the full moon next month. This PK match is sure to attract everyone in the martial arts to watch the battle, so they find you an economic man with potential for business operations, let you organize this century war that has been seen in a century, assuming that both of them have a certain blood HP1 , HP2.HP1 is Fengzhiyu, HP2 is copper hydroxide. They also have certain attack power AP1, AP2, AP1 is Fengzhiyu, AP2 is copper hydroxide. When attacking, the opponent’s HP reduces its own attack power, such as HP1=2 HP2=1 AP1=1 AP2=1, when copper hydroxide attacks Maple Feather, Maple Feather’s HP=2 (the original HP1) -1 (AP2 of copper hydroxide)=1. Now the two people compete for many rounds, and each round is either Maple Feather attacking Copper Hydroxide, or Copper Hydroxide Attacking Maple Feather. Ask Feng Zhiyu to win the chance of winning copper hydroxide as the next leader of the martial arts.

Input

This question contains multiple sets of test data, each line is HP1, HP2, AP1 and AP2 (1<=HP1,HP2,AP1,AP2<=32767, all are integers, HP1/AP2<=1000&&HP2/AP1<=1000)

Output

Each set of data outputs one row, which is the value of the probability of Fengzhiyu winning copper hydroxide (the result is kept to 4 decimal places).

Sample Input

2 1 1 1

Sample Output

75.0000

Idea: Probability DP

Code:

#include<iostream>
#include<stdio.h>
using namespace std;
 
double ans[1005][1005];
 
double f(int x, int y)
{
	if (x <= 0)return 0;
	if (y <= 0)return 100;
	if (ans[x][y]>=0)return ans[x][y];
	return ans[x][y] = (f(x - 1, y) + f(x, y - 1)) / 2;
}
 
int main()
{
	int hp1, hp2, ap1, ap2;
	while (scanf("%d%d%d%d",&hp1,&hp2,&ap1,&ap2)!=EOF)
	{
		hp1 = (hp1 + ap2 - 1) / ap2;
		hp2 = (hp2 + ap1 - 1) / ap1;
		for (int i = 0; i <= hp1; i++)
		for (int j = 0; j <= hp2; j++)ans[i][j] = -1;
		printf("%.4f\n", f(hp1, hp2));
	}
	return 0;
}

CSU 1342:Double

topic:

Description

    There is a sequence composed of M integers. Each time a number is randomly selected from it (the probability of each number in the sequence being selected is equal) and accumulated. A total of N times are taken . What is the probability that the final result is divisible by 3?

Input

    The input contains multiple sets of data.
    For each set of test data, the first line contains two integers MN  (1 <=  M  <= 100, 1 <=  N  <= 30), the meaning is the same as above. The next line contains M integers in the range [1, 100], describing each integer in this sequence in turn.

Output

    For each set of data, use the format of "Case  XY " to output the answer, where X represents the number of test data (starting from 1), and Y represents the probability that the final result can be divisible by 3, rounded to the nearest 8 decimal places.

Sample Input

2 2
1 2
1 2
4
1 3
4
5 10
4 5 3 1 5

Sample Output

Case 1: 0.50000000
Case 2: 0.00000000
Case 3: 1.00000000
Case 4: 0.33333340

Hint

    This topic is mainly to recommend that you use "double" to handle floating-point numbers, and try not to use "float", because the accuracy of "float" is low and often cannot meet the accuracy requirements of the topic, so it can be used directly in the ACM competition. "double" to deal with floating-point numbers. "Double" uses "%lf" to control the reading, and "%f" to control the output.
    Let's solve the other details first and then discuss the idea of ​​this problem.
    First of all, this question also has multiple sets of data, but unlike "A Sample Problem", the number of sets of data is not directly given, then how to deal with it? At this time, we generally use codes like while(scanf("%d%d", &M, &N) != EOF){} to deal with, "!= EOF" is the key, as for its specific meaning, I won’t introduce too much Anyway, after having this framework, we can directly write the code for each group of data in the while loop. At this time, you may have this question: If you write the code in this way, how can I end the input when I manually enter the sample? Ctrl + Z, and then press Enter to OK!
    Secondly, how to deal with keeping 8 decimal places? Generally, in ACM competitions, if there is no clear description of how to deal with it (for example, to use "tailing method"), or to "round up", we will use a form similar to printf("%.8f", x) to reserve the designated position The decimal of the number. As for the students who use cout output in C++, please refer to the relevant information about controlling the number of decimal places.
    Next we will analyze how to do this topic.
    First of all, whether the final can be divisible by 3 is actually related to the result of the final sum of integers taken out (maybe remember this sum as "S") modulo 3. The so-called "modulus" is the operator "%" in C/C++, which means "divide by a certain number and take the remainder". If S%3==0, then it is divisible by 3. If S%3==1 or S%3==2, then it cannot be divisible by 3. In other words, what we want to calculate is the probability of S%3==0.
    Let us analyze the fourth example. It’s a lot to take ten times, let’s take a look first.
    If you take it once, the number of 0 modulo 3 is only 3, so the probability of S%3==0 is 0.2 (record this probability as p0), and there are two numbers modulo 3 that get 1: 4 and 1, so S%3 The probability of == 1 is 0.4 (denote this probability as p1). Similarly, there are two numbers modulo 3 to 2: 5 and 5, so the probability of S%3==2 is also 0.4 (denote this probability as p2) .
    Let's analyze the situation of taking twice?
    How to calculate the probability of S%3==0 if it is taken twice? p0*p0 + p1*p2 + p2*p1 = 0.36. What is the meaning of this calculation? Because it is necessary to ensure that the last S%3==0, then if the digital modulo 3 taken out for the first time is 0, the number taken the second time must also be 0 modulo 3, and the same, if the digital modulo 3 taken out for the first time If it gets 1, then the number taken out for the second time must be modulo 3 to get 2. If the number taken out for the first time is modulo 3, then the number taken out for the second time must get 1 modulo 3. In this way we get the above formula. In the same way, we can calculate the probability of S%3==1 as p0*p1 + p1*p0 + p2*p2 = 0.32, and the probability of S%3==2 is also 0.32.
    Let's analyze the situation of three times?
    If it is taken three times, the probability of S%3==0 should be 0.36*p0 + 0.32*p2 + 0.32*p1. The meaning of this calculation must have been thought of by everyone, because we have to ensure that the final S%3==0, then if the sum of the numbers taken out in the first two times is 0 modulo 3, then the number taken out for the third time must also be modulo 3. 0. Similarly, if the sum of the numbers taken out twice before modulo 3 is 1, then the number taken out for the third time must be modulo 3 to get 2. If the sum of the numbers taken out two times before modulo 3 is 2, then The number taken out for the third time must be 1 modulo 3. In the same way, we can easily write out how to calculate the probability of S%3==1 and S%3==2.
    At this point in the analysis, you must have thought about how to calculate the fourth, fifth, and tenth time? In a similar way, the fourth result can be calculated based on the third result, the fifth time can be calculated based on the fourth result, and so on. So even if it counts up to a hundred times, it won't be a problem! For loop 100 times is OK.

// All of the above are hints

In fact, this topic can be regarded as probability DP. The code is estimated to be similar, I used a little space optimization, but there is no difference.

Code:

#include <iostream>
#include<iomanip>
using namespace std;

double p1, p2, p3;
int a, b, c;

void get_p()
{
	int s = a + b + c;
	double x1 = a*p3 + b*p2 + c*p1;
	double x2 = b*p3 + c*p2 + a*p1;
	p3 = (a*p2 + b*p1 + c*p3) / s;
	p2 = x2 / s;
	p1 = x1 / s;
}

int main()
{
	ios_base::sync_with_stdio(false);
	int m, n, i = 1, num;
	while (cin >> m >> n)
	{
		a = 0, b = 0, c = 0;
		while (m--)
		{
			cin >> num;
			if (num % 3 == 1)a++;
			else if (num % 3 == 2)b++;
			else c++;
		}
		p1 = 0, p2 = 0, p3 = 1;
		while (n--)get_p();
		cout << "Case " << i++ << ": " << fixed << setprecision(8) << p3 << endl;
	}
	return 0;
}

CSU 1725 Garrosh Hellscream vs. Ethereal Thief Rafham

topic:

 

Description

It’s been a long time since Garrosh Hellscream was not pleasing to the Ethereal Robber Rafam, and finally one day, Garrosh Hellscream brought his terrifying slave master to block the Ethereal Robber Rafam, the Ethereal Robber. Seeing that the situation was not good, Rafam pulled out the death knell of the artifact and prepared to fight back. The scene is very spectacular, as shown in the picture (omitted)

It is known that every time the death knell hits an enemy unit, the opponent's HP will be reduced by one, and the probability of hitting each enemy unit is equal. The terrifying slave owner has 3 health points. When he receives non-lethal damage on the field and the total number of terrifying slave owners on the field is less than 7, it will summon a new terrifying slave owner with 3 health points and suffer fatal damage (by When the life is 0 after the attack, it will die directly. For example, there is a "horrible slave owner" with 3 health on the field. When the death knell hits him, his health becomes 2 and a new slave owner is called. And when the death knell of horror hits Garrosh, the horrible slave owner will only watch and do nothing.

It is now known that the death knell of horror will be launched X times in total. Garrosh has Y points of stamina, and he has Z slave owners with three lives. After the death knell is used up, how likely is it to kill Garrosh? (The answer is 6 digits after the decimal point)

Input

For multiple sets of data, there is an integer T in the first row, which means there are T sets of data. (T<=100) The
following T rows, each row has three integers X, Y and Z. (1<=X,Y<=20,0<=Z<=7)

Output

One decimal (keep six digits after the decimal point).

Sample Input

4
1 1 1
2 1 1
2 1 2
2 2 2

Sample Output

0.500000
0.666667
0.500000
0.111111

The idea of ​​this topic is not complicated, it is memory search.

A very interesting thing is used to simplify the code a little bit: the value of (z1 + z2 + z3 <7) is 1 or 0, true corresponds to 1, and false corresponds to 0.

Code:

#include<iostream>
#include<string.h>
#include<iomanip>
using namespace std;
 
double list[21][21][8][8][8];
 
double f(int x, int y, int z1, int z2, int z3)
{
	if (list[x][y][z1][z2][z3] >= 0)return list[x][y][z1][z2][z3];
	if (y == 0)return 1;	
	if (x < y)return 0;
	double p1 = f(x-1, y - 1, z1, z2, z3);
	double p2 = 0, p3 = 0, p4 = 0;
	if(z1)p2 = f(x-1, y, z1 - 1, z2, z3);
	if(z2)p3 = f(x-1, y, z1 + 1, z2 - 1, z3 + (z1 + z2 + z3 < 7));
	if(z3)p4 = f(x-1, y, z1, z2 + 1, z3 - 1 + (z1 + z2 + z3 < 7));
	list[x][y][z1][z2][z3] = (p1 + p2*z1 + p3*z2 + p4*z3) / (1 + z1 + z2 + z3);
	return list[x][y][z1][z2][z3];
}
 
int main()
{
	int cas;
	cin >> cas;
	int x, y;
	int z1, z2, z3;
	while (cas--)
	{
		cin >> x >> y >> z3;
		z1 = z2 = 0;
		memset(list, -1, sizeof(list));
		cout << fixed << setprecision(6) << f(x, y, z1, z2, z3) << endl;
	}
	return 0;
}
<iostream>
#include<string.h>
#include<iomanip>
using namespace std;

double list[21][21][8][8][8];

double f(int x, int y, int z1, int z2, int z3)
{
	if (list[x][y][z1][z2][z3] >= 0)return list[x][y][z1][z2][z3];
	if (y == 0)return 1;	
	if (x < y)return 0;
	double p1 = f(x-1, y - 1, z1, z2, z3);
	double p2 = 0, p3 = 0, p4 = 0;
	if(z1)p2 = f(x-1, y, z1 - 1, z2, z3);
	if(z2)p3 = f(x-1, y, z1 + 1, z2 - 1, z3 + (z1 + z2 + z3 < 7));
	if(z3)p4 = f(x-1, y, z1, z2 + 1, z3 - 1 + (z1 + z2 + z3 < 7));
	list[x][y][z1][z2][z3] = (p1 + p2*z1 + p3*z2 + p4*z3) / (1 + z1 + z2 + z3);
	return list[x][y][z1][z2][z3];
}

int main()
{
	int cas;
	cin >> cas;
	int x, y;
	int z1, z2, z3;
	while (cas--)
	{
		cin >> x >> y >> z3;
		z1 = z2 = 0;
		memset(list, -1, sizeof(list));
		cout << fixed << setprecision(6) << f(x, y, z1, z2, z3) << endl;
	}
	return 0;
}

Because f is written very accurately, it can hardly be faster anymore, so 0ms perfect AC for this topic

 

Second, expect DP

LIghtOJ 1038 Race to 1 Again

topic:

Description

Rimi learned a new thing about integers, which is - any positive integer greater than 1 can be divided by its divisors. So, he is now playing with this property. He selects a number N. And he calls this D.

In each turn he randomly chooses a divisor of D (1 to D). Then he divides D by the number to obtain new D. He repeats this procedure until Dbecomes 1. What is the expected number of moves required for N to become 1.

Input

Input starts with an integer T (≤ 10000), denoting the number of test cases.

Each case begins with an integer N (1 ≤ N ≤ 105).

Output

For each case of input you have to print the case number and the expected value. Errors less than 10-6 will be ignored.

Sample Input

3

1

2

50

Sample Output

Case 1: 0

Case 2: 2.00

Case 3: 3.0333333333

 

At first, I thought it was to decompose N into the product of k prime numbers, and then the answer was a function of k, and this function could be solved in advance and stored as an array.

However, I found that the examples given are wrong for me!

It turns out that the probability of different prime numbers being selected is different. . .

50=2*5*5, not an independent choice of 3 prime numbers, but an equal probability choice based on a divisor of 50.

I figured it out, there are up to 6 different prime factors for numbers that do not exceed 10^5, so the answer can be expressed as a function of the degree of these 6 prime factors ff

If there are less than 6, the following variables are automatically 0

Code:

#include<iostream>
#include<string.h>
#include<iomanip>
using namespace std;
 
double r[17][10][7][5][5][5];
int list[6];
double ff(int a, int b, int c, int d, int e, int f)
{
	if (a + b + c + d + e + f == 0)return 0;
	if (r[a][b][c][d][e][f] >= 0)return r[a][b][c][d][e][f];
	r[a][b][c][d][e][f] = 0;
	for (int i1 = a; i1 >= 0; i1--)
	for (int i2 = b; i2 >= 0; i2--)
	for (int i3 = c; i3 >= 0; i3--)
	for (int i4 = d; i4 >= 0; i4--)
	for (int i5 = e; i5 >= 0; i5--)
	for (int i6 = f; i6 >= 0; i6--)
		r[a][b][c][d][e][f] += ff(i1, i2, i3, i4, i5, i6);
	int k = (a + 1)*(b + 1)*(c + 1)*(d + 1)*(e + 1)*(f + 1);
	r[a][b][c][d][e][f] += k;
	r[a][b][c][d][e][f] /= (k - 1);
	return r[a][b][c][d][e][f];
}
 
bool prime(int m)
{
	for (int i = 2; i*i <= m; i++)if (m%i == 0)return false;
	return true;
}
 
int main()
{
	int t;
	int n;
	cin >> t;
	memset(r, -1, sizeof(r));
	for (int cas = 1; cas <= t;cas++)
	{
		cin >> n;
		if (n == 1)
		{
			cout << "Case " << cas << ": " << 0 << endl;
			continue;
		}
		
		memset(list, 0, sizeof(list));
		int k = 0;
		for (int i = 2; i <= n; i++)
		{
			if (i + i > n)i = n;
			if (prime(i) && n%i == 0)
			{
				while (n%i == 0)
				{
					list[k]++;
					n /= i;
				}
				k++;
			}
		}
		cout <<"Case "<<cas<<": "<<fixed<<setprecision(8)
		<<ff(list[0],list[1],list[2],list[3],list[4],list[5]) << endl;
	}
	return 0;
}

It's a pity that it has timed out.

Think about it carefully, my algorithm, after calculating 100, then calculating 36 should be very fast, just take it directly from the 6-dimensional array (input 100 and 36 are the same)

However, the factorization of each factor takes time.

If you just find all the factors of n without judging whether the factors are prime numbers, it should be faster.

Sure enough, write in the most common way, and it's over

Code:

#include<iostream>
#include<string.h>
#include<iomanip>
using namespace std;
 
double r[100001];
 
double f(int m)
{
	if (r[m] >= 0)return r[m];
	int k = 0;
	r[m] = 0;
	for (int i = 2; i * i <= m; i++)
	{
		if (m%i == 0)
		{
			r[m] += f(i);
			k++;
			if (i*i != m)
			{
				r[m] += f(m / i);
				k++;
			}
		}
	}
	r[m] += k + 2;
	r[m] /= k + 1;
	return r[m];
}
 
int main()
{
	int t;
	int n;
	cin >> t;
	memset(r, -1, sizeof(r));
	r[1] = 0;
	for (int cas = 1; cas <= t;cas++)
	{
		cin >> n;
		cout << "Case " << cas << ": " << fixed << setprecision(8) << f(n) << endl;
	}
	return 0;
}

HDU 4405 Aeroplane chess (flying chess)

topic:

Description

Hzz loves aeroplane chess very much. The chess map contains N+1 grids labeled from 0 to N. Hzz starts at grid 0. For each step he throws a dice(a dice have six faces with equal probability to face up and the numbers on the faces are 1,2,3,4,5,6). When Hzz is at grid i and the dice number is x, he will moves to grid i+x. Hzz finishes the game when i+x is equal to or greater than N. 

There are also M flight lines on the chess map. The i-th flight line can help Hzz fly from grid Xi to Yi (0<Xi<Yi<=N) without throwing the dice. If there is another flight line from Yi, Hzz can take the flight line continuously. It is granted that there is no two or more flight lines start from the same grid. 

Please help Hzz calculate the expected dice throwing times to finish the game. 

Input

There are multiple test cases. 
Each test case contains several lines. 
The first line contains two integers N(1≤N≤100000) and M(0≤M≤1000). 
Then M lines follow, each line contains two integers Xi,Yi(1≤Xi<Yi≤N).   
The input end with N=0, M=0. 

Output

For each test case in the input, you should output a line indicating the expected dice throwing times. Output should be rounded to 4 digits after decimal point. 

Sample Input

2 0
8 3
2 4
4 5
7 8
0 0

Sample Output

1.1667
2.3441

The subject is still a bit complicated.

However, there is one place that can be simplified, that is, because we basically use the form of accumulative swipe table to calculate, so if there are two flight lines (flight lines) connected, this is not a consideration.

In other words, we should assume that there is no such phenomenon and just write the code. If this phenomenon occurs, the answer is the same.

My first idea is to use the list to record the probability of a certain grid, and use the times to record the average number of times required from 0 to this grid.

Code:

#include<iostream>
#include<string.h>
#include<iomanip>
using namespace std;
 
int n, m;
double list[100011];		//概率
double times[100011];		//平均次数
int fly[100011];		//飞行线
 
double f(int i,int k)
{
	if (i < 0)return 0;
	if (i >= n - 5 && k == n)return list[i] * (7 - n + i) / 6;
	return list[i] / 6;
}
 
double ft(int i,int k)
{
	if (i < 0)return 0;
	return f(i, k) * (times[i] + 1);
}
 
int main()
{
	int start, end_;
	double r1, r2;
	while (cin >> n >> m)
	{		
		if (n == 0)break;
		memset(fly, 0, sizeof(fly));
		for (int i = 0; i < m; i++)
		{
			cin >> start >> end_;
			fly[start] = end_;
		}
		memset(list, 0, sizeof(list));
		memset(times, 0, sizeof(times));
		list[0] = 1;
		times[0] = 0;
		for (int i = 1; i <= n; i++)
		{
			list[i] += f(i - 1, i) + f(i - 2, i) + f(i - 3, i) + f(i - 4, i) + f(i - 5, i) + f(i - 6, i);
			times[i] += ft(i - 1, i) + ft(i - 2, i) + ft(i - 3, i) + ft(i - 4, i) + ft(i - 5, i) + ft(i - 6, i);
			if (list[i] > 0)times[i] /= list[i];
			if (fly[i])
			{
				list[fly[i]] += list[i];
				times[fly[i]] += list[i] * times[i];
				list[i] = 0;
			}
		}		
		cout << fixed << setprecision(4) << times[n] << endl;
	}
	return 0;
}

This is 78ms

Because there were several wrong answers before AC, I thought it was a division by 0 problem, so I used another code AC before this code was completed.

My second idea: times does not indicate the number of times, but the expectation of the times.

Code:

#include<iostream>
#include<string.h>
#include<iomanip>
using namespace std;
 
int n, m;
double list[100011];		//概率
double times[100011];		//次数的期望
int fly[100011];		//飞行线
 
double f(int i,int k)
{
	if (i < 0)return 0;
	if (i >= n - 5 && k == n)return list[i] * (7 - n + i) / 6;
	return list[i] / 6;
}
 
double ft(int i,int k)
{
	if (i < 0)return 0;
	if (list[i] == 0)return 0;
	if (i >= n - 5 && k == n)return (times[i] + list[i])*(7 - n + i) / 6;
	return (times[i] + list[i]) / 6;
}
 
int main()
{
	int start, end_;
	double r1, r2;
	while (cin >> n >> m)
	{		
		if (n == 0)break;
		memset(fly, 0, sizeof(fly));
		for (int i = 0; i < m; i++)
		{
			cin >> start >> end_;
			fly[start] = end_;
		}
		memset(list, 0, sizeof(list));
		memset(times, 0, sizeof(times));
		list[0] = 1;
		times[0] = 0;
		for (int i = 1; i <= n; i++)
		{
			list[i] += f(i - 1, i) + f(i - 2, i) + f(i - 3, i) + f(i - 4, i) + f(i - 5, i) + f(i - 6, i);
			times[i] += ft(i - 1, i) + ft(i - 2, i) + ft(i - 3, i) + ft(i - 4, i) + ft(i - 5, i) + ft(i - 6, i);
			if (fly[i])
			{
				list[fly[i]] += list[i];
				times[fly[i]] += times[i];
				list[i] = 0;
			}
		}		
		cout << fixed << setprecision(4) << times[n] << endl;
	}
	return 0;
}

The last modification of this code is the code in the last if. AC is 140ms.

Because the two codes are almost the same, I changed the previous code to AC. (The 3 codes in this article are all AC)

When I wrote this, I was surprised to find that I was actually pushing back from 0!

Obviously, it should be pushed backwards from n. . .

Then I wrote the algorithm of the reverse calculation. It took only 2 minutes to finish the writing, and I submitted the direct AC, and it only took 31ms. Reverse push is much simpler than forward push.

The meaning of times here is different. It refers to the average number of times required from each grid to the end.

Code:

#include<iostream>
#include<string.h>
#include<iomanip>
using namespace std;
 
int n, m;
double times[100011];		//平均次数
int fly[100011];		//飞机
 
int main()
{
	int start, end_;
	double r1, r2;
	while (cin >> n >> m)
	{
		if (n == 0)break;
		memset(fly, 0, sizeof(fly));
		for (int i = 0; i < m; i++)
		{
			cin >> start >> end_;
			fly[start] = end_;
		}
		memset(times, 0, sizeof(times));
		for (int i = n - 1; i >= 0; i--)
		{
			if (fly[i])times[i] = times[fly[i]];
			else times[i] = (times[i + 1] + times[i + 2] + times[i + 3] + times[i + 4] + times[i + 5] + times[i + 6]) / 6 + 1;
		}
		cout << fixed << setprecision(4) << times[0] << endl;
	}
	return 0;
}

About why reverse seeking is much simpler than forward seeking: https://blog.csdn.net/nameofcsdn/article/details/52082746

POJ 2096 Collecting Bugs

topic:

Description

Ivan is fond of collecting. Unlike other people who collect post stamps, coins or other material stuff, he collects software bugs. When Ivan gets a new program, he classifies all possible bugs into n categories. Each day he discovers exactly one bug in the program and adds information about it and its category into a spreadsheet. When he finds bugs in all bug categories, he calls the program disgusting, publishes this spreadsheet on his home page, and forgets completely about the program. 
Two companies, Macrosoft and Microhard are in tight competition. Microhard wants to decrease sales of one Macrosoft program. They hire Ivan to prove that the program in question is disgusting. However, Ivan has a complicated problem. This new program has s subcomponents, and finding bugs of all types in each subcomponent would take too long before the target could be reached. So Ivan and Microhard agreed to use a simpler criteria --- Ivan should find at least one bug in each subsystem and at least one bug of each category. 
Macrosoft knows about these plans and it wants to estimate the time that is required for Ivan to call its program disgusting. It's important because the company releases a new version soon, so it can correct its plans and release it quicker. Nobody would be interested in Ivan's opinion about the reliability of the obsolete version. 
A bug found in the program can be of any category with equal probability. Similarly, the bug can be found in any given subsystem with equal probability. Any particular bug cannot belong to two different categories or happen simultaneously in two different subsystems. The number of bugs in the program is almost infinite, so the probability of finding a new bug of some category in some subsystem does not reduce after finding any number of bugs of that category in that subsystem. 
Find an average time (in days of Ivan's work) required to name the program disgusting.

Input

Input file contains two integer numbers, n and s (0 < n, s <= 1 000).

Output

Output the expectation of the Ivan's working days needed to call the program disgusting, accurate to 4 digits after the decimal point.

Sample Input

1 2

Sample Output

3.0000

It is equivalent to the expectation of finding max(X,Y), where X and Y are both hypergeometric distributions.

However, it is very difficult to solve mathematically (it seems to be possible, but the calculation of the combination is really complicated)

This problem needs to be solved by recursion:

Code:

#include<iostream>
#include<iomanip>
#include<string.h>
using namespace std;
 
int n, s;
double list[1001][1001];
 
double f(int i, int j)
{
	if (list[i][j] >= 0)return list[i][j];
	if (i > n || j > s)return 0;
	double d = n*s;
	d += i*(s - j)*f(i, j + 1);
	d += (n - i)*j*f(i + 1, j);
	d += (n - i)*(s - j)*f(i + 1, j + 1);
	list[i][j] = d / (n*s - i*j);
	return list[i][j];
}
 
int main()
{	
	cin >> n >> s;
	memset(list, -1, sizeof(list));
	list[n][s] = 0;
	cout << fixed << setprecision(4) << f(0, 0);
	return 0;
}

SGU 495 Kids and Prizes

topic:

Description

ICPC (International Cardboard Producing Company) is in the business of producing cardboard boxes. Recently the company organized a contest for kids for the best design of a cardboard box and selected  M winners. There are  N prizes for the winners, each one carefully packed in a cardboard box (made by the ICPC, of course). The awarding process will be as follows:

  • All the boxes with prizes will be stored in a separate room.
  • The winners will enter the room, one at a time.
  • Each winner selects one of the boxes.
  • The selected box is opened by a representative of the organizing committee.
  • If the box contains a prize, the winner takes it.
  • If the box is empty (because the same box has already been selected by one or more previous winners), the winner will instead get a certificate printed on a sheet of excellent cardboard (made by ICPC, of course).
  • Whether there is a prize or not, the box is re-sealed and returned to the room.

The management of the company would like to know how many prizes will be given by the above process. It is assumed that each winner picks a box at random and that all boxes are equally likely to be picked. Compute the mathematical expectation of the number of prizes given (the certificates are not counted as prizes, of course).

Input

The first and only line of the input file contains the values of  N and  M  ( ).

Output

The first and only line of the output file should contain a single real number: the expected number of prizes given out. The answer is accepted as correct if either the absolute or the relative error is less than or equal to 10^-9.

Example(s)

sample input
sample output
5 7
3.951424
sample input
sample output
4 3
2.3125

This question means that m individuals independently choose n boxes (prizes), and how many boxes will be selected on average.

I remember that the above part of the introduction to algorithms talked about this problem, considering the probability of a box being selected.

For a box, the probability that one person will not choose it is 1-1.0/n, so the probability that m individuals will not choose it is (1-1.0/n)^m.

So 1 box will be selected 1-(1-1.0/n)^m on average.

Because all boxes are the same, the answer is (1-(1-1.0/n)^m)*n

Code:

#include<iostream>
#include<iomanip>
using namespace std;
 
int main()
{
	int n, m;
	cin >> n >> m;
	double a = 1 - 1.0 / n;
	double b = 1;
	while (m--)b *= a;
	cout << fixed<<setprecision(10)<<n*(1 - b);
	return 0;
}

This code. . . It's terribly short

Of course, it can also be done with dynamic programming, but it is not necessary.

 

Guess you like

Origin blog.csdn.net/nameofcsdn/article/details/113098838