小记:一开始自己是用的dp,dp数组定义也对了。但是就是状态转移方程少加一个步。然后看了一下discuss,别人用了dp,更多的是母函数,不过我一开始用的是dp那么就用dp解。然后看了一眼别人的dp,发现和自己定义的是一样的,所以就自己继续想状态转移方程,终于理解出来了。
思路:母函数模板题,我的blog里可看讲解,dp,这里先讲讲dp,
dp[i][j]表示对数i,用不大于j的数组成i的 种数
状态转移方程:
对于i<j,dp[i][j] = dp[i][i]
对于i==j,dp[i][j] = 1 + dp[i][j-1]
对于i>j,dp[i][j] = dp[i-j][j] + dp[i][j-1]
主要的一个方程就是i>j时的。
dp[i][j] 包括两个部分,
第一部分就是不包括j且小于用小于j的数组成i的种数
第二部分就是包括了一个j,那么我们剩下的还要凑齐的就是i-j这个数,而用来凑的数不能大于j,
所以,第一部分的种数就是dp[i-j][j],第二部分的种数即为dp[i][j-1]
例:
4 = 4;
4 = 3 + 1;
4 = 2 + 2;
4 = 2 + 1 + 1;
4 = 1 + 1 + 1 + 1;
dp[4][3] = dp[4][2] + dp[1][3]
通过上面理解,dp[i][j]一部分是包括了j的和不包括j的两部分
初始化,dp[0][i] = dp[i][0] = 0
dp[1][i] = dp[i][1] = 1
dp代码:
#include <iostream>
#include <cstdio>
using namespace std;
const int MAX_ = 130;
int f[MAX_][MAX_];
void init(){
for(int i = 1;i < MAX_;++i){
f[0][i] = 0;
f[i][0] = 0;
f[i][1] = 1;
f[1][i] = 1;
}
for(int i = 1; i < MAX_; ++i){
int cnt = 0;
for(int j = 1; j < MAX_; ++j){
if(i < j)f[i][j] = f[i][i];
else if(i == j)f[i][j] = 1 + f[i][j-1];
else {
f[i][j] = f[i-j][j] + f[i][j-1];
}
}
}
}
int main(){
//freopen("f:\\out.txt","w",stdout);
long long n;
init();
while(cin>>n){
cout<<f[n][n]<<endl;
}
return 0;
}
母函数模板直接提交,直接A,改都不用改:
#include <iostream>
using namespace std;
// Author: Tanky Woo
// www.wutianqi.com
const int _max = 10001;
// c1是保存各项质量砝码可以组合的数目
// c2是中间量,保存没一次的情况
int c1[_max], c2[_max];
int main()
{ //int n,i,j,k;
int nNum; //
int i, j, k;
while(cin >> nNum)
{
for(i=0; i<=nNum; ++i) // ---- ①
{
c1[i] = 1;
c2[i] = 0;
}
for(i=2; i<=nNum; ++i) // ----- ②
{
for(j=0; j<=nNum; ++j) // ----- ③
for(k=0; k+j<=nNum; k+=i) // ---- ④
{
c2[j+k] += c1[j];
}
for(j=0; j<=nNum; ++j) // ---- ⑤
{
c1[j] = c2[j];
c2[j] = 0;
}
}
cout << c1[nNum] << endl;
}
return 0;
}