关于整数拆分的递归法与母函数法
先是题目:
Ignatius and the Princess III Crawling in process... Crawling failed Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit Status 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
先是递归法,很容易理解,但是,很明显,超时了。。。。
#include <iostream> using namespace std; int s(int a,int b) { if(a==1||b==1) return 1; if(a<b) return s(a,a); if(a==b) return (s(a,b-1)+1); if(a>b) return (s(a,b-1)+s(a-b,b)); } int main() { int n; while(cin>>n) cout<<s(n,n)<<endl; return 0; }
再是母函数法:
母函数是建立在多项式的乘法上的,先要理解(1+X+X^2+X^3......)*(1+X^2+X^4+X^6......)*(1+X^3+X^6+X^9......).......得出的aX^n,a为凑成n的方法数。母函数的核心是一个三重循环,其中,关于母函数的题目有三种:一,1,2,3,4,5......这样+1,+1的,并且不限数量,如:整数拆分;二:不限数量,但是每个数都是另给出的,如 Square Coins;第三种是数是另给出的,并且限制了数量,数量也是另给出的。
先是讲最简单的第一种情况:
整数拆分,就不再给题目了:
1 #include<iostream> 2 using namespace std; 3 int a[125],b[125]; 4 int main() 5 { 6 int i,j,k,n; 7 for(i=0;i<=120;i++) 8 a[i]=b[i]=1; 9 for(i=2;i<=120;i++) 10 { 11 for(j=i;j<=120;j+=i) 12 for(k=0;k+j<=120;k++) 13 b[j+k]+=a[k]; 14 for(j=0;j<=120;j++) 15 a[j]=b[j]; 16 } 17 while(cin>>n) 18 cout<<a[n]<<endl; 19 return 0; 20 }
另外一种方法,但是个人觉得第一种较优;
#include<iostream> using namespace std; #define M 200 int c1[M],c2[M]; int main() { int n; while(cin>>n) { memset(c1,0,sizeof(c1)); memset(c2,0,sizeof(c2)); c1[0]=1; int j,i,k; for(i=1;i<=n;i++) { for(j=0;j<=n;j++) for(k=0;k+j<=n;k+=i) c2[k+j]=c2[k+j]+c1[j]; for(j=0;j<=n;j++) c1[j]=c2[j]; memset(c2,0,sizeof(c2)); } cout<<c1[n]<<endl; } return 0; } //这个代码不是我写的。。。。。。
当然第一种有一个局限性,就是最小的那个数据必须是1,因为:
for(i=0;i<=120;i++)
a[i]=b[i]=1;
如果不是1,就改为最小的那个数;
就后来的三重循环的第一重就从2开始,但是可以不必初始化,也不必清0.
第一个代码的三重循环是:
i指第i个括号,j指乘到第i个括号里的指数为j的项,k指指数为k;
接下来就是第二中可能:不限制个数,但是每个数都是题目另给的,并不是+1+1+1的;
题目:
D - Square Coins Crawling in process... Crawling failed Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit Status Practice HDU 1398 Description
People in Silverland use square coins. Not only they have square shapes but also their values are square numbers. Coins with values of all square numbers up to 289 (=17^2), i.e., 1-credit coins, 4-credit coins, 9-credit coins, ..., and 289-credit coins, are available in Silverland. There are four combinations of coins to pay ten credits:
ten 1-credit coins, one 4-credit coin and six 1-credit coins, two 4-credit coins and two 1-credit coins, and one 9-credit coin and one 1-credit coin.
Your mission is to count the number of ways to pay a given amount using coins of Silverland.
Input
The input consists of lines each containing an integer meaning an amount to be paid, followed by a line containing a zero. You may assume that all the amounts are positive and less than 300.
Output
For each of the given amount, one line containing a single integer representing the number of combinations of coins should be output. No other characters should appear in the output.
Sample Input
2 10 30 0 Sample Output
1 4 27
分析:首先要注意,硬币分别是1,4,9,16......即n^2,由于题目给出数目是<=289的,所以先设置一个数组:c[17]={1,4,9,16,25,36,49,64,81,100,121,144,169,196,225,256,289};
由于第一重循环是只第i个括号,即每加一个硬币所增的幂数,所以改成 for(l=1,k=c[l];l<17;l++,k=c[l])也就是每增一个硬币就增加了c[l];
其余不变,代码如下:
1 #include <iostream> 2 #include <stdio.h> 3 using namespace std; 4 int a[300],b[300]; 5 int main() 6 { 7 int i,j,k,l,c[17]={1,4,9,16,25,36,49,64,81,100,121,144,169,196,225,256,289},n; 8 for(i=0;i<=300;i++) 9 a[i]=b[i]=1; 10 for(l=1,k=c[l];l<17;l++,k=c[l]) 11 { 12 for(j=k;j<=300;j+=k) 13 for(i=0;i+j<=300;i++) 14 b[i+j]+=a[i]; 15 for(i=0;i<=300;i++) 16 a[i]=b[i]; 17 18 } 19 while(cin>>n,n) 20 cout<<a[n]<<endl; 21 return 0; 22 }
提交的时候,显示用了15MS,但是另一种方法,时间为0MS。
如下:
1 #include<stdio.h> 2 #define max 310 3 int main() 4 { 5 int coin[17] = {1,4,9,16,25,36,49,64,81,100,121,144,169,196,225,256,289}; 6 int n,i,j,k, an[max], bn[max]; 7 while(scanf("%d", &n) && n) 8 { 9 for(i = 0; i <= n; i ++) 10 { 11 an[i] = 1; 12 bn[i] = 0; 13 } 14 for(j = 1; j < 17; j ++) 15 { 16 for(i = 0; i <= n; i ++) 17 for(k = 0; k + i <= n; k += coin[j]) 18 bn[k + i] += an[i]; 19 for(i = 0; i <= n; i ++) 20 { 21 an[i] = bn[i]; 22 bn[i] = 0; 23 } 24 } 25 printf("%d\n", an[n]); 26 } 27 return 0; 28 }
但个是这和第一种情况的代码有一点相似,就是bn[i]要不断清0;
第一重循环依旧是第j个括号(只有17),第二重循环是指数为i的,第三重是第j个括号里的指数为k,和我写的代码的第二和第三的顺序相反。
第三种情况:
题目:
C - 选课时间(题目已修改,注意读题) Crawling in process... Crawling failed Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u Submit Status Practice HDU 2079 Description
又到了选课的时间了,xhd看着选课表发呆,为了想让下一学期好过点,他想知道学n个学分共有多少组合。你来帮帮他吧。(xhd认为一样学分的课没区别)
Input
输入数据的第一行是一个数据T,表示有T组数据。 每组数据的第一行是两个整数n(1 <= n <= 40),k(1 <= k <= 8)。 接着有k行,每行有两个整数a(1 <= a <= 8),b(1 <= b <= 10),表示学分为a的课有b门。
Output
对于每组输入数据,输出一个整数,表示学n个学分的组合数。
Sample Input
2 2 2 1 2 2 1 40 8 1 1 2 2 3 2 4 2 5 8 6 9 7 6 8 8 Sample Output
2 445
代码:
#include <iostream> #include <stdio.h> using namespace std; int c1[645],c2[645]; int n,k,a[9],b[9],i; int main() { int T; cin>>T; while(T--) { cin>>n>>k; for(i=1;i<=k;i++) scanf("%d%d",&a[i],&b[i]); for(i=0;i<=n;i++) c1[i]=c2[i]=0; c1[0]=1; for(i=1;i<=k;i++) { for(int j=0;j<=n;j++) for(int l=0;l+j<=n&&l<=a[i]*b[i];l=l+a[i]) c2[j+l]+=c1[j]; for(int j=0;j<=n;j++) c1[j]=c2[j],c2[j]=0; } cout<<c1[n]<<endl; } return 0; }
所以还是用那个代码模板比较好(第一种情况和第二种情况的第二种方法);
在这里的第一重循环中:i<k,k是k中数,在这是指由k种课。n是要修的总学分。
二重循环不变,第三重循环在第二种基础下多了一个限制条件:l<=a[i]*b[i],也就是数量的限制。
好了,母函数就到这里了~~~接下来看看今天做的题~~~明天继续~\(≧▽≦)/~啦啦啦