*【洛谷 - P1025】数的划分(dfs 或 dp 或 母函数,第二类斯特林数Stirling)
題干:
題目描述
將整數n分成k份,且每份不能為空,任意兩個方案不相同(不考慮順序)。
例如:n=7,k=3,下面三種分法被認為是相同的。
1,1,5
1,5,1
5,1,1
問有多少種不同的分法。
輸入輸出格式
輸入格式:
?
n,kn,k?(6<n \le 2006<n≤200,2 \le k \le 62≤k≤6)
?
輸出格式:
?
11個整數,即不同的分法。
?
輸入輸出樣例
輸入樣例#1:?復制
7 3輸出樣例#1:?復制
4說明
四種分法為:
1,1,51,1,5;
1,2,41,2,4;
1,3,31,3,3;
2,2,32,2,3.
解題報告:
? ? 這題也是今年十月份左右的時候清理工邀請賽的第二簽到題。。當時想不到辦法了直接dfs過的。偶然翻洛谷看到了原題、、見到了dp解法和母函數的解法。
? ?dfs很簡單,做個簡單的剪枝就好了,這是個看似o(200^6)但是實際上遠遠沒有這么高復雜度的dfs。今天先補上dp的解法,母函數的以后復習的時候再說。
AC代碼1:(dfs)
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define ll long long #define pb push_back #define pm make_pair #define fi first #define se second using namespace std; const int MAX = 2e5 + 5; ll dp[505][505]; int n,k; void dfs(int last,int sum,int cur) {if(cur==k) {if(sum==n) cnt++;return;}for(int i=last; sum+i*(k-cur)<=n; i++) {//剪枝,只用枚舉到sum+i*(k-cur)<=n為止dfs(i,sum+i,cur+1);} } int main() {cin>>n>>k;dfs(1,0,0);cout << cnt <<endl;return 0 ;}AC代碼2:(dp)
dp[i][j]代表把i個小球放到j個盒子中的方法數。
一個簡單的想法:以一個數為基準,逐步減少分割數 或 減少待分割數 。(這里選擇1)
分為兩種情況(因為是無序的所以認為每次都是劃分完后所有球從小到大排序了)
a.至少有一個盒子只有一個小球的情況數
b.沒有一個盒子只有一個小球的情況數
這樣進行劃分是因為這種分類可以使a和b都有能寫出來的表達式:
a.因為盒子不加區分,那么1的情況數與“將n-1個小球放到k-1個盒子中”的情況數一樣
b.沒有一個盒子只有一個小球,那么把每個盒子中拿出來一個小球,對應的是“把(n-k)個小球放到k個盒子中的情況數”
這個b情況可以理解為:第一個數不為1時,可以視為先在所有的位置上都加上一個1再對于所有的位置用新的總數求次數,
所以定了的是占有了總數j個,位置仍然是j個,與原來相比沒有變化。所以b情況的方案數=把i-j個球放到j個盒子中的方案數。
然后將上面的思路化為動態轉移方程:
#include<cstdio> #include<iostream> #include<algorithm> #include<queue> #include<map> #include<vector> #include<set> #include<string> #include<cmath> #include<cstring> #define ll long long #define pb push_back #define pm make_pair #define fi first #define se second using namespace std; const int MAX = 2e5 + 5; ll dp[505][505]; int main() {int n,k;for(int i = 1; i<=303; i++) /*dp[i][0] = */dp[i][1] = 1;//那一句到底有沒有必要加啊,反正是這個狀態用不到?dp[1][1] = 1;for(int i = 2; i<=303; i++) {for(int j = 2; j<=i; j++) {if(i-j >= 0) dp[i][j] = dp[i-1][j-1] + dp[i-j][j];//其實要是j<=i這么寫的話這一行前面的那個if也可以不要了//else dp[i][j] = 0;}}cin>>n>>k;cout << dp[n][k]<<endl;return 0 ;}總結:
? ?注意一下dp這里的數組不能開dp[505][25]這樣,如果要這么寫的話那就加上else也是錯的,,因為你想啊i會遍歷到300,那j也會跟著到300,所以會越界啊就修改別的數據了,,所以保險起見要么你在內層循環寫上j<=min(k,i)或者min(15,i),要么就直接開個打數組來存這些沒用的狀態就好了嘛反正也是用不到的狀態,,恰好也不會卡住空間,,干脆就開這么大好了。
題解https://www.luogu.org/problemnew/solution/P1025
總結
以上是生活随笔為你收集整理的*【洛谷 - P1025】数的划分(dfs 或 dp 或 母函数,第二类斯特林数Stirling)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【CodeForces - 768C】J
- 下一篇: 《赛博朋克2077》义体及插件控制台代码