小A點菜
阿新 • • 發佈:2020-11-14
目錄
思路\(1\)
十分經典的一道DP題。
轉移方程式為:
f[j]+=f[j-a[i]];
我們首先定義\(f[i][j]\)表示前\(i\)個菜品恰好花費\(j\)元的方案數。
當我們買第\(i\)道菜時:
f[i][j]+=f[i-1][j-a[i]];
當我們不買第\(i\)道菜時:
f[i][j]+=f[i-1][j];
但當第\(i\)道菜的價格被就是\(j\)時,就可以只買自己,這種情況應該其實包含在\(1\)中,但是由於\(f[i][0]=0\),所以不會算入這種情況。所以我們就要把\(f[i][0]\)剛開始就賦值為\(1\)。
程式碼
#include <bits/stdc++.h> using namespace std; int n,m,a[105],f[105][10005]; int main() { scanf("%d %d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=0;i<=n;i++) f[i][0]=1;//注意0 for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { f[i][j]+=f[i-1][j]; if(j>=a[i]) f[i][j]+=f[i-1][j-a[i]]; } } printf("%d",f[n][m]); return 0; }
我們可以優化為一維。
但我們的第二重迴圈必須逆序跑,因為我們一維的轉移方程式為:
f[j]+=f[j-a[i]];
如果跑正序,我們的\(f[j]\)還沒有更新,\(f[j-a[i]]\)反倒先更新了,這樣的話與題意的“每種菜只有一份”相沖。如果沒有這句話,我們就可以正序了。
#include <bits/stdc++.h> using namespace std; int n,m,a[105],f[10005]; int main() { scanf("%d %d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); f[0]=1; for(int i=1;i<=n;i++) { for(int j=m;j>=a[i];j--) f[j]+=f[j-a[i]]; } printf("%d",f[m]); return 0; }
思路\(2\)
\((1)if(j==第i道菜的價格)\ f[i][j]=f[i-1][j]+1;\)
\((2)if(j>第i道菜的價格)\ f[i][j]=f[i-1][j]+f[i-1][j-第i道菜的價格];\)
\((3)if(j<第i道菜的價格)\ f[i][j]=f[i-1][j];\)
說的簡單一些,這三個方程,每一個都是在吃與不吃之間抉擇。若錢充足,辦法總數就等於吃這道菜的辦法數與不吃這道菜的辦法數之和;若不充足,辦法總數就只能承襲吃前\(i-1\)道菜的辦法總數。依次遞推,在最後,我們只要輸出\(f[n][m]\)的值即可。
程式碼:
#include <bits/stdc++.h> using namespace std; int n,m,a[105],f[105][10005]; int main() { scanf("%d %d",&n,&m); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=n;i++) { for(int j=1;j<=m;j++) { if(j==a[i])f[i][j]=f[i-1][j]+1; else if(j>a[i]) f[i][j]=f[i-1][j]+f[i-1][j-a[i]]; else f[i][j]=f[i-1][j]; } } printf("%d",f[n][m]); return 0; }