HDU 1028 Ignatius and the Princess III
2016-07-28 12:21
302 查看
之前做过这个题目,当时看的题解方法是母函数,等我理解母函数之后会再写母函数的感想。
今天看的方法是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
[m]来表示将整数n分成加数不大于m的分法数,这样可以避免重复,因为dp
[m]可以分为加数存在m和加数全比m小的情况,显然这些情况不会重复,而且全比m小意味着存在了新的m值,新的m意味着加数至多为m-1,那么状态转移显然已经完成了。dp
[m]=dp[n-m][m]+dp
[m-1],这里可以发现n-m务必大于等于0,否则数组越界。
但是n-m作为数组的第一个元素,实际上是要大于1的,因为给的整数n规模是大于1的,也就是说不会出现拆分0的情况,那么显然出现0的情况需单独考虑。
1.n==m 意味着0的出现。加数即为m,那么分为加数为m和小于m的两种情况。
小于m即为dp
[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
[m]的意思为n拆分成最大加数不超过一个大于n的m的分法,很明显dp
[m]=dp
[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];
综上所有情况考虑完成。
代码如下:
也可先打一个表记录一下,反正这道题的规模不是很大,每次重新计算也可以。
今天看的方法是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
[m]来表示将整数n分成加数不大于m的分法数,这样可以避免重复,因为dp
[m]可以分为加数存在m和加数全比m小的情况,显然这些情况不会重复,而且全比m小意味着存在了新的m值,新的m意味着加数至多为m-1,那么状态转移显然已经完成了。dp
[m]=dp[n-m][m]+dp
[m-1],这里可以发现n-m务必大于等于0,否则数组越界。
但是n-m作为数组的第一个元素,实际上是要大于1的,因为给的整数n规模是大于1的,也就是说不会出现拆分0的情况,那么显然出现0的情况需单独考虑。
1.n==m 意味着0的出现。加数即为m,那么分为加数为m和小于m的两种情况。
小于m即为dp
[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
[m]的意思为n拆分成最大加数不超过一个大于n的m的分法,很明显dp
[m]=dp
[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 ); } return 0; }
也可先打一个表记录一下,反正这道题的规模不是很大,每次重新计算也可以。
相关文章推荐
- Mootools 1.2教程 函数
- autoit InputBox 函数
- 文件遍历排序函数
- 关于C#中排序函数的总结
- Oracle 函数大全[字符串函数,数学函数,日期函数]第1/4页
- ASP下经常用的字符串等函数参考资料
- PostgreSQL教程(五):函数和操作符详解(1)
- DOS批处理 函数定义与用法
- asp Chr 函数 数字转字母的方法
- Lua中的函数精讲笔记
- Lua中的闭合函数、非全局函数与函数的尾调用详解
- Lua中调用C++函数示例
- Lua实现split函数
- Lua常用时间函数使用实例
- Lua函数与字符串处理简明总结
- Lua学习笔记之表和函数
- Lua中实现sleep函数功能的4种方法
- Lua函数用法研究
- Lua基础教程之赋值语句、表达式、流程控制、函数学习笔记
- Sql Server中REPLACE函数的使用