POJ 3014 Cake Pieces and Plates

太尼玛坑爹啦!!!

身为一个组合数学弱菜,看了dp水题的题解,去做的题,本想有没有数论公式直接套,上了wiki看了半天发现没有,然后记忆化搜索,自己的数据都过不去。。

正值吃完晚饭的时间,昨晚又没睡几个小时,睡了一阵,起床继续看,搜了下题解,尼玛第一篇只用了四行代码两层循环啊!!!!

想起wiki里说的p(m,n)=p(m-1,n-1)+p(n,m-n),怎么也想不通啊。。。

不得已看了别人长的代码,分了三种情况讨论n>k,n==k,n<k,每种情况又分了两种情况,有空盘和没空盘。

然后才到dp的范围里。。。

 

对于n>m的情况,必然是有空盘的,它的方案数等于p(m,n-1),因为多出一个空盘不影响方案数啊。此时p(m,n)=p(m,n-1);

对于n==m的情况,没空盘的时候只有一种情况,n个1;有空盘的时候同上。此时p(m,n)=p(m,n-1)+1;

对于n<m的情况,没空盘的时候是这样处理的:先把每个盘子里放一个蛋糕,保证没空盘,剩下的m-n个蛋糕就随便往n个盘子里放就好啦,这样方案数相当于把剩下的m-n个蛋糕放进n个盘子里的方案数;那么有空盘的时候还是同上。此时p(m,n)=p(m-n,n)+p(m,n-1);

 

你以为开二维数组就能解了嘛??话说discuss里说卡常数项卡的很紧啊- -,下面说说那奇葩的四行代码是怎么写出来的。

先代码:

#include<stdio.h>
int dp[5000]={1};
int main(){
	int i,j,n,m;
	while(~scanf("%d%d",&n,&m)){
		for(j=1;j<=m;j++)dp[j]=0;
		for(i=1;i<=n;i++)
		    for(j=i;j<=m;j++)
		        dp[j]=(dp[j]+dp[j-i])%1000000007;
		printf("%d\n",dp[m]);
	}
	return 0;
}

会dp的人很容易反应到这和01背包空间降维的方法很像。就是在第i次循环中,在更新dp[j]之前,dp[j]里保存的其实是i-1里的dp[j],那么p(m,n-1)我们就不用处理啦,因为之前保存的dp[j]就是p(m,n-1)。而对于i==j的情况,让dp[0]=1,dp[j]+dp[j-i]=dp[j]+1,就相当于p(m,n)=p(m,n-1)+1。对于j>i的情况,dp[j-i]已经是第i次循环被更新过的dp[j-i]了,也就是说代表的恰好是p(m-n,n)而不是p(m-n,n-1)。所以这个算法是正确的。

 

情不自禁感慨一下啊!数学之美在于把复杂的事情简化成数学符号进行处理,算法之美在于把复杂的情况简化成同类统一处理~~

你可能感兴趣的:(POJ 3014 Cake Pieces and Plates)