[NYOJ176] 整數劃分(二) [遞推計數][整數劃分]
阿新 • • 發佈:2018-11-12
經典dp,整數劃分。
套路的想法是把複雜的問題劃分為多個1相加。
對於這道題考慮到如果劃分出來的某個數是1,那麼去掉這個1,這個數就沒了。
所以把問題拆分之後,劃分出一個新的數的條件就是多分出一個一。
計數問題,想要計所有情況的數一般不難,難的是去重。
這道問題我們現在只需要考慮取了哪些值,而不能多關心其它的,否則就會有重複狀態。
在轉移的時候,我們顯然不需要也不能關心之前劃分具體是怎麼樣的:
我們只需要考慮之前用掉了多少值,劃分出了幾個數就可以了。
狀態就有了。
。
按照拆分問題的思路,一個新的數出現,那麼它的初始值應該是一。
至於要讓現在這些數的值增加,我們顯然不好“從裡面找一個”加一。
因為這樣的話就是關心劃分的具體情況了。這不好。
所以我們就得把前面的看作整體,全部加上一。
隨著前面這些數出現時間可重集的不同,我們剛好能夠統計出
時的劃分方案數
實現的時候用填表法或者刷表法都不會重複計數。
填表
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
int T, n, m;
int F[105][105];
int main() {
scanf("%d", &T);
while (T--) {
memset(F, 0, sizeof(F));
scanf("%d%d", &m, &n);
F[0][0] = 1;
for (int i = 1; i <= m; ++i) {
for (int j = 1; j <= i; ++j) {
F[i][j] = F[i-1][j-1] + F[i-j][j];
}
}
printf("%d\n", F[m][n]);
}
return 0;
}
刷表
#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstdlib>
#include<cmath>
#include<cctype>
#include<cstring>
using namespace std;
int T, n, m;
int F[105][105];
int main() {
scanf("%d", &T);
while (T--) {
memset(F, 0, sizeof(F));
scanf("%d%d", &m, &n);
F[0][0] = 1;
for (int i = 0; i <= m; ++i) {
for (int j = 0; j <= i; ++j) {
if (i+1 <= m && j+1 <= n) F[i+1][j+1] += F[i][j];
if (i+j <= m) F[i+j][j] += F[i][j];
}
}
printf("%d\n", F[m][n]);
}
return 0;
}