HDU 1028 Ignatius and the Princess III

之前做过这个题目,当时看的题解方法是母函数,等我理解母函数之后会再写母函数的感想。
今天看的方法是DP。
DP我一般很难找到状态方程。现在可能有所感悟。
如果题目给的规模范围为n,那么子问题大多是从n=1开始的。换句话说,就是当问题被抽象成一个规模整体变小的子问题时,有的时候我们用dp[i][j]表达这个状态,有的时候可能是dp[i]来表示,[i][j]还是[i]取决于子问题的特征,比如上升子序列问题一般用dp[i][j]代表长度为i并且以a[j]为末尾元素的子序列,如果不加上以a[j]为结尾的条件,那么实际上dp[i][j]和dp[i][j-1]就没什么区别或者说是特征,子问题之间转移就没什么意义了
我还是再积累积累经验吧。
下面是题目:

Problem Description
“Well, it seems the first problem is too easy. I will let you know how foolish you are later.” feng5166 says.

“The second problem is, given an positive integer N, we define an equation like this:
N=a[1]+a[2]+a[3]+…+a[m];
a[i]>0,1<=m<=N;
My question is how many different equations you can find for a given N.
For example, assume N is 4, we can find:
4 = 4;
4 = 3 + 1;
4 = 2 + 2;
4 = 2 + 1 + 1;
4 = 1 + 1 + 1 + 1;
so the result is 5 when N is 4. Note that “4 = 3 + 1” and “4 = 1 + 3” is the same in this problem. Now, you do it!”

Input
The input contains several test cases. Each test case contains a positive integer N(1<=N<=120) which is mentioned above. The input is terminated by the end of file.

Output
For each test case, you have to output a line contains an integer P which indicate the different equations you have found.

Sample Input

4
10
20

Sample Output

5
42
627

题目大意是给你一个数字n,我们将n分成各种整数x1,x2,x2…xn,每个x最小为1,最大为n问有几种方法。

反正以我的菜鸡最容易直接想到的思路就是一个一个从大到小分,比如将6分成5和1,然后5减1,1加1,变成4+2,然后从第二大于1的数开始执行同样的操作,但是这样做很容易重复,而且没办法避免重复。

首先我们用dp[n][m]来表示将整数n分成加数不大于m的分法数,这样可以避免重复,因为dp[n][m]可以分为加数存在m和加数全比m小的情况,显然这些情况不会重复,而且全比m小意味着存在了新的m值,新的m意味着加数至多为m-1,那么状态转移显然已经完成了。dp[n][m]=dp[n-m][m]+dp[n][m-1],这里可以发现n-m务必大于等于0,否则数组越界。
但是n-m作为数组的第一个元素,实际上是要大于1的,因为给的整数n规模是大于1的,也就是说不会出现拆分0的情况,那么显然出现0的情况需单独考虑。

1.n==m 意味着0的出现。加数即为m,那么分为加数为m和小于m的两种情况。
小于m即为dp[n][m-1],加数为m即为dp[n-m][m]也就是dp[0][m],此为一种情况。
我们可以有两种写法。
(1)for(int i=0,j=0;j<=n;j++) dp[i][j]=1; //先处理0的情况。
else if(i>=j) dp[i][j]=dp[i-j][j]+dp[i][j-1];//然后0的情况可以并到大于的正常情况中
(2) else if(i==j) dp[i][j]=1+dp[i][j-1]; //也可以直接和大于分开处理。
else if(i>j) dp[i][j]=dp[i-j][j]+dp[i][j-1];

那么n>m的正常情况和n==m的有点正常的情况都考虑了,那么 n小于m的情况呢。
老实说我并不在意这种可能,n小于m意味着dp[n][m]的意思为n拆分成最大加数不超过一个大于n的m的分法,很明显dp[n][m]=dp[n][m] (n大于m),毕竟大于n了想拆分也拆不了。
但是我们还是要加到代码中。而不能直接规定m的上限为n。
因为比如dp[5][3]=dp[2][3]+dp[5][2],看似正常的情况出现了。
如果不处理这种情况,dp[2][3] 默认值为0,显然错了。
所以加上这句else if(i < j) dp[i][j]=dp[i][i];
综上所有情况考虑完成。
代码如下:

#include <vector>
#include <list>
#include <map>
#include <set>
#include <deque>
#include <queue>
#include <stack>
#include <bitset>
#include <algorithm>
#include <functional>
#include <numeric>
#include <utility>
#include <complex>
#include <sstream>
#include <iostream>
#include <iomanip>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <cassert>
using namespace std;
const int MAXN = 120+10;
int n,dp[MAXN][MAXN];
int main()
{
    while(~scanf("%d",&n)){
        memset(dp,0,sizeof(dp));
        for(int i=0,j=0;j<=n;j++) dp[i][j]=1;
        for(int i=1;i<=n;i++){
            for(int j=1;j<=n;j++){
                if(i==1||j==1) dp[i][j]=1;
                //else if(i==j) dp[i][j]=1+dp[i][j-1];
                else if(i<j) dp[i][j]=dp[i][i];
                else if(i>=j) dp[i][j]=dp[i-j][j]+dp[i][j-1];
            }
        }
        printf("%d\n",dp[n][n]);
    }
    return 0;
}

也可先打一个表记录一下,反正这道题的规模不是很大,每次重新计算也可以。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值