算法训练 印章
共有n种图案的印章,每种图案的出现概率相同。小A买了m张印章,求小A集齐n种印章的概率。
解法:
这题要用二维的动态规划来做,建立一个二维数组dp[m][n],开始的时候以为dp[i][1],应该为1,因为买m枚印章肯定会出现1种,后来看了别人的题解发现想错了,dp[i][j]表示的是买i枚里面有j种的概率,而不是买i枚至少有j枚的概率。
而中间态可以分为两种情况,一种是前i-1个已经凑齐n种,一种是前i-1个凑齐i-1种,故可得状态转移方程如下:
#include<stdio.h>
#include <cmath>
int main()
{
int n, m;
scanf_s("%d%d", &n, &m);
double dp[25][25], p;
p = 1.0 / n;
for (int i = 1; i <= m; i++)
{
for (int j = 1; j <= n; j++)
{
if (i < j)
{
dp[i][j] = 0.0;
}
else if (j == 1)
{
dp[i][j] = pow(p, i - 1);
}
else {
dp[i][j] = dp[i - 1][j] * (j * 1.0 / n) + dp[i - 1][j - 1] * ((n - j + 1) * 1.0 / n);
}
}
}
printf("%.4lf", dp[m][n]);
return 0;
}
算法训练 拿金币
有一个N x N的方格,每一个格子都有一些金币,只要站在格子里就能拿到里面的金币。你站在最左上角的格子里,每次可以从一个格子走到它右边或下边的格子里。请问如何走才能拿到最多的金币。
解法:
简单的dp解法,用一个二维数组存到达每个格子可拿的最大金币数,最终dp[n-1][n-1],即为最优解。
#include<stdio.h>
int max[1005][1005];
int main()
{
int n;
scanf_s("%d", &n);
for (int i = 0; i < n; i++)
{
for (int j = 0; j < n; j++)
{
scanf_s("%d", &max[i][j]);
}
}
for (int i = 1; i < n; i++)
{
max[i][0] += max[i - 1][0];
max[0][i] += max[0][i - 1];
}
for (int i = 1; i < n; i++)
{
for (int j = 1; j < n; j++)
{
if (max[i][j - 1] > max[i - 1][j])
{
max[i][j] += max[i][j - 1];
}
else
{
max[i][j] += max[i - 1][j];
}
}
}
printf("%d", max[n - 1][n - 1]);
return 0;
}