http://www.cnblogs.com/staginner/archive/2011/11/30/2269491.html
给你一个价格n,求在指定使用的硬币个数范围内付款的方案数,后面可能给你边界a,b,也可能不给,不给的话个数是1-n,给一个a就是1-a,给两个是a-b
直接开二维数组背包的话会算重复,比如凑6的话,1、2、3,,2、1、3,3、1、2,一种方案就数了三遍,需加一维限制大小,dp[i][j][k]中用j个硬币凑成价格i,j个硬币中最大的硬币面值最大是k,dp[i][j][k]+=dp[i-k][j-1][t], 1<=t<=k.但这样规模会太大
这个题目涉及到一个结论,用不超过j个硬币凑出面值i的方案种数,是和用面值不超过j的硬币凑出面值i的方案种数是相同的。说得再数学一点,就是整数i拆分成不超过j个整数的拆分数,是和整数i拆成若干个值不超过j的整数的拆分数是相同的。具体的证明用到了Ferrers图像的性质。
这样的话我们就可以取一个二维数组f[i][j]表示用面值不超过j的硬币凑出面值i的方案的种数,那么如果我使用了面值j,对应方案种数就应该加上f[i-j][j],如果我们不使用面值j,那么对应的方案种数就应该加上f[i][j-1]。也就是说状态转移方程为f[i][j]= f[i-j][j]+ f[i][j-1]。
#include<stdio.h>
#include<string.h>
#include<iostream>
#define N 310
using namespace std;
long long f[N][N];
int n,a,b;
char s[200];
int main()
{
int i,j,k,l;
memset(f,0,sizeof(f));
/*
for(i=0;i<=300;i++)
f[0][i]=1;
for(i=0;i<=k;i++)
{
for(j=1;j<=i;j++)
{
if(i>=j)
f[i][j]+=f[i-j][j]; //用j
f[i][j]+=f[i][j-1];//没用j
}
}
这样不对,j比i大的时候,f[i][j]还是0,即每次用了j时f[i][j]加上的情况f[i-j][j]一直是0,少算了,f[i][j],当i比j小的时候依然有用
*/
f[0][0]=1;
k=300;
for(i=0;i<=k;i++)
{
for(j=1;j<=k;j++)
{
if(i>=j)
f[i][j]+=f[i-j][j]; //用j
f[i][j]+=f[i][j-1];//没用j
}
}
while(gets(s)!=NULL)
{
a=b=-1;
sscanf(s,"%d%d%d",&n,&a,&b);
//sscanf中没有读取成功将不会改变原值
if(a==-1&&b==-1)
a=1,b=n;
else if(a!=-1&&b==-1)
b=a,a=1;
printf("%lld\n",f[n][b]-f[n][a-1]);
}
return 0;
}
百度百科
Ferrers图像
图像概念
图像性质
图像应用
(a) 整数n拆分成k个数的和的拆分数,和数n拆分成最大数为k的拆分数相等。因整数n拆分成k个数的和的拆分可用一k行的图像表示。所得的Ferrers图像的共轭图像最上面一行有k个格子。例如:
5个数,最大数为6
24=5+5+5+4+3+2
6个数,最大数为5
(b) 整数n拆分成最多不超过m个数的和的拆分数,和n拆分成最大不超过m的拆分数相等。 理由与(a)类似。
(c) 整数n拆分成互不相同的若干奇数的和的拆分数,和n拆分成自共轭的Ferrers图像的拆分数相等。
设n=(2n1+1)+(2n2+1)+……+2(nk+1),其中n1>n2>……nk。
构造一个Ferrers图像,其第一行,第一列都是n1+1格,对应于2n1+1,第二行,第二列各n2+1格,对应于2n2+1。依此类推。由此得到的Ferrrers图像是共轭的。反过来也一样。
例如:17=9+5+3