蓝桥杯刷题训练——印章(动态规划)

373 阅读2分钟

「这是我参与2022首次更文挑战的第4天,活动详情查看:2022首次更文挑战」。

题目

有n种图案的印章,每种印章出现的概率相同。拿m枚印章,那么集齐n种印章的概率是多少。

思路

很明显,这道题目可以用动态规划来解。动态规划是将一个问题分解成一个个子问题来解决,并且每个子问题都是互相有联系的。那么这道题如何一步步化成子问题呢。可以取一个二维数组d[m][n],二维数组的第一列代表的就是拿了几枚印章,之后的每一列就代表的就是拿了这几枚印章,能够收集到几种不同的印章。接下来就可以从一个个子问题来入手,首先,当前拿到的印章数小于要收集的印章种数,那概率必然是0,i<j,d[i][j]=0,如果j=1,不管有几个印章,只收集一种的概率那就是(1/n)的i次方,但是有n种情况,所以概率是(1/n)的(i-1)次方,如果i>j,d[i][j]就之前的三种子情况有关系了,首先是d[i-1][j],少了一个印章,但是种类不变,说明多出来的一个印章种类还是在j个种类中。然后是d[i-1][j-1],少了一个印章,也少了一个印章种类,说明多了一个印章之后选中的一定是原先没有的种类。只有这两种子问题情况,d[i][j]=d[i-1][j](j/n)+d[i-1][j-1]((n-j+1)/n)。最终得出的答案就存储在d[m][n]中。

代码

#include<bits/stdc++.h>
using namespace std;
double d[100][100];
int main(){
	int n,m;
	cin>>n>>m;
	double p=1.0/n;
	for(int i=1;i<=m;i++){
		for(int j=1;j<=n;j++){
			if(i<j)
				d[i][j]=0;
			if(j==1)
				d[i][j]=pow(p,(i-1));
			else
				d[i][j]=d[i-1][j]*(1.0*(j)/n)+d[i-1][j-1]*(1.0*(n-j+1)/n);
		}
	}
	printf("%.4lf",d[m][n]);//保留四位小数
	return 0;
}

总结

一道题目想用动态规划去求解,从最小的问题开始入手,然后规模逐渐扩大,观察大一些的问题和小问题是否存在联系,如果存在联系,那么就是制定合适的算法思路来完成这道题。