【整数划分系列】
1.nyoj90
描述
将正整数n表示成一系列正整数之和:n=n1+n2+…+nk,
其中n1≥n2≥…≥nk≥1,k≥1。
正整数n的这种表示称为正整数n的划分。求正整数n的不
同划分个数。
例如正整数6有如下11种不同的划分:
6;
5+1;
4+2,4+1+1;
3+3,3+2+1,3+1+1+1;
2+2+2,2+2+1+1,2+1+1+1+1;
1+1+1+1+1+1。
- 输入
- 第一行是测试数据的数目M(1<=M<=10)。以下每行均包含一个整数n(1<=n<=10)。
- 输出
- 输出每组测试数据有多少种分法。
- 样例输入
-
1 6
- 样例输出
11
- 【思路】
- dp[i][j]代表整数i划分的所有数中最大数不超过j的划分数。
#include<bits/stdc++.h> using namespace std; int dp[15][15]; int main() { int n,m,i,j,t; dp[1][1]=1; for(i=1; i<=10; i++) for(j=1; j<=10; j++) { if(j==1) dp[i][j]=1; else if(i>j) dp[i][j]=dp[i-j][j]+dp[i][j-1];//含j和不含j else if(i==j) dp[i][j]=1+dp[i][j-1]; else dp[i][j]=dp[i][i]; } cin>>t; while(t--) { scanf("%d",&n); printf("%d\n",dp[n][n]); } return 0; }
因为n很小不超过10,可以用递归写,最多算10!也不会TLE,下面是dfs:
#include<iostream> #include<cstdio> #include<algorithm> #include<cmath> #include<cstring> #include<vector> using namespace std; typedef long long LL; int n,ans; void dfs(int i,int s) { if(s>n) return; if(s==n) { ans++; return; } for(i; i<=10; i++)//保证每次加的数大于等于原来的 { dfs(i,s+i); } } int main() { int m; cin>>m; while(m--) { ans=0; cin>>n; dfs(1,0); cout<<ans<<endl; } }
2.nyoj176
n划分成m个整数的和
dp[i][j]代表i划分成j个数
#include<bits/stdc++.h> using namespace std; int dp[105][108]; int main() { int n,m,i,j,t; dp[1][1]=1; for(i=2; i<=100; i++) for(j=1; j<=i; j++) { if(i==j||j==1) dp[i][j]=1; else dp[i][j]=dp[i-1][j-1]+dp[i-j][j];//含1和不含1 } cin>>t; while(t--) { scanf("%d%d",&n,&m); printf("%d\n",dp[n][m]); } return 0; }
3.nyoj279:队花的烦恼
描述
ACM队队花C小+最近在X大OJ上做题,竟发现了一道做不出来的…水题!她快郁闷死了……也许是最近状态不太好吧……她希望大家能帮帮忙:把一个整数分成若干个不为零的整数,问有多少种不同分法。
例:7 3 其中的分法:1 1 5,1 5 1,5 1 1是同一种分法。
- 输入
- 有多组测试数据
每组数据都有两个整数n,m(6<=n<=500,2<=m<=6)
n表示该整数,m表示把n分成m份 - 输出
- 对每一组测试数据,输出不同的分法数
- 样例输入
-
7 3 10 2 20 3
- 样例输出
-
4 5 33
- 注意m<=6即可
#include<bits/stdc++.h> using namespace std; int dp[505][8]; int main() { int n,m,i,j; dp[1][1]=1; for(i=2; i<=500; i++) for(j=1; j<=i&&j<=6; j++) { if(i==j||j==1) dp[i][j]=1; else dp[i][j]=dp[i-1][j-1]+dp[i-j][j]; } while(~scanf("%d%d",&n,&m)) { printf("%d\n",dp[n][m]); } return 0; }
4.51nod1201整数划分
将N(N<=50000)分为若干个不同整数的和,有多少种不同的划分方式,例如:n = 6,{6} {1,5} {2,4} {1,2,3},共4种。由于数据较大,输出Mod 10^9 + 7的结果即可。
开始想的dp[i][j]代表j的最大划分数不超过i,但是数组需要开很大。
换个思路就是dp[i][j]代表j划分成i个整数的方法数,也分有1还是没有1,跟上面第2题有点类似,但是还要求划分成不同的整数,所以方程为dp[i][j] = dp[i][j-i](无1) + dp[i-1][j-i](有1且仅有1个1)
认真思考会发现i不会超过320,利用等差数列求和求出
#include <bits/stdc++.h> using namespace std; const int N = 50000; const int mod = 1e9+7; typedef long long LL; LL dp[322][N+4]; int main() { int n; for(int j = 1; j <= N; j++) dp[1][j] = 1; for(int i = 2; i <= 320; i++) for(int j = 1; j <= N; j++) { if(i >= j) continue; else dp[i][j] = (dp[i][j-i] + dp[i-1][j-i])%mod; } while(~scanf("%d", &n)) { int sum = 0, ans = 0; for(int i = 1; i <= 320; i++) { ans = (ans + dp[i][n]) % mod; } printf("%d\n", ans); } return 0; }