《盜賊遺產2》以諾的計劃互動點位說明
題目連結:https://www.acwing.com/problem/content/description/902/
題目描述
一個正整數 n 可以表示成若干個正整數之和,形如:n=n1+n2+…+nk,其中 n1≥n2≥…≥nk,k≥1。
我們將這樣的一種表示稱為正整數 n 的一種劃分。
現在給定一個正整數 n,請你求出 n 共有多少種不同的劃分方法。
輸入描述
共一行,包含一個整數 n。
輸出描述
共一行,包含一個整數,表示總劃分數量。
由於答案可能很大,輸出結果請對 109+7 取模。
1≤n≤1000
示例
輸入
5
輸出
7
借鑑完全揹包問題求解
切入點
通過分析整數劃分這個問題,我們可以發現它與完全揹包問題具有一定的相似性。待劃分的整數相當於揹包的容量,要劃分成的數字相當於要裝入揹包的物品。因此,我們就會想到化用完全揹包問題的狀態轉移方程。
狀態表示
f(i,j)表示滿足如下條件劃分方案的數量:
- 劃分成的數字小於等於i
- 劃分後的數字的總和等於j
注意於完全揹包的不同,此處的f(i,j)表示的是方案數不是最大值。
狀態劃分與計算
與完全揹包相同,對於f(i,j)我們劃分如下:
- 用了0個i,方案數即f(i-1,j)
- 用了1個i,方案數即f(i-1,j-i)
- 用了2個i,方案數即f(i-1,j-2*i)
·········
因此,初步的狀態轉移方程為:
f(i,j)=f(i-1,j)+f(i-1,j-i)+··· (j-k*i>=0)
在完全揹包問題中,我們通過等價變形狀態轉移方程進行了優化,此處我們也進行嘗試:
f(i,j)=f(i-1,j)+f(i-1,j-i)+f(i-1,j-2*i)···
f(i,j-i)=f(i-1,j-i)+f(i-1,j-2*i)+···
因此,優化後的狀態轉移方程為:
f(i,j)=f(i-1,j)+f(i,j-i)
最後,與完全揹包一樣,可以將f(i,j)降為一維優化,不再贅述,AC程式碼為一維版本。
初始邊界處理
顯然:
f(i,0)=0
f(0,i)=0
考慮一下f(0,0),不劃分與之對應,f(0,0)=1,只要從f(i,j)的意義上入手,所有f(i,j)保持一致,應該就能確定正確的初始值。
如果感覺不保險,就用邊界值代進去試試,在這裡就用f(1,1),f(1,1)算出來對就行。
另一種思路
狀態表示
f(i,j)為滿足如下的條件的劃分方案數:
- 劃分成的數字總和為i
- 劃分成的數字的個數為j
狀態劃分與計算
對於f(i,j)所代表的狀態,我們進行如下劃分:
- 方案中所劃分成的最小數字是1
- 方案中所劃分的最小數字大於1
記劃分一的方案數為x,劃分二的方案數為y。
我們將劃分一中每個方案的那個1去掉,得到的新方案與原方案數量一致,將新方案集合與f(i-1,j-1)對照。首先,劃分一中的方案將變成和為i-1,共j-1項,符合f(i-1,j-1)的狀態要求,所以x<=f(i-1,j-1)。再將f(i-1,j-1)所代表的方案均加上一個1,將變成和為i,共j項,符合劃分一,即f(i-1,j-1)<=x。所以,x=f(i-1,j-1)。
同理,我們將劃分二中每一項減去1,將新方案集合與f(i-j,j)對照。可以得到y=f(i-j,j),證明方法同上,不再贅述。
綜上,狀態轉移方程為:
f(i,j)=f(i-1,j-1)+f(i-j,j)
另外,這個無法優化成一維,因為得到f(i,j)需要上面幾行的資料,必須儲存下來以供計算。
完全揹包思路的AC程式碼
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1001;
const int mod = 1e9 + 7;
int n;
int f[N];
int main()
{
cin >> n;
f[0]=1;
for (int i = 1; i < N; i++)
for (int j = i; j <= n; j++)
f[j] = (f[j] + f[j-i]) % mod;
cout << f[n] << endl;
return 0;
}
非完全揹包思路的AC程式碼
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1001;
const int mod = 1e9 + 7;
int n;
int f[N][N]; //邊界處理 自動初始化為0 沒寫程式碼但要知道應該初始化
int main()
{
cin >> n;
f[0][0] = 1; //邊界處理
for (int i = 1; i <= n; i++)
for (int j = 1; j <= i; j++)
f[i][j] = (f[i - 1][j - 1] + f[i - j][j]) % mod;
int res = 0;
for (int i = 1; i <= n; i++)
res = (res + f[n][i]) % mod;
cout << res << endl;
return 0;
}