P1679 神奇的四次方數
阿新 • • 發佈:2020-12-08
取還是不取,取的話取多少,完全揹包.
注意初始化的問題.
完全揹包問題,dp[i][j]表示前i個數(第i個數即為i4)中j的最小分解位數.比如(壓維後)dp[1] = 1, dp[2] = 2(即14 + 14),dp[17] = 2(即24 + 14), dp[706] = 2(即34 + 54).
有dp[i + 1][j] = min(dp[i][j], dp[i][j - s[i]] + 1),s[i]為i4.
壓維.
顯然,如果不對dp進行初始化,最終dp仍然全部為0,如何解決呢?一種想法是:
if(dp[j]) if(dp[j - s[i]]) dp[]=...else dp[]=... else if(dp[j - s[i]]) dp[]=... else dp[]=...
事實上,只需要:
for(int i = 1; i <= m; i++) dp[i] = 100000000; // 注意 dp[0] 仍然為0
可知所有的無效位最後均會在取min時被忽略,而初始的唯一有效位dp[0]最終會遞推出其它所有有效位.例如i = 1迴圈結束後:
(此處dp大小為1000僅因為方便除錯)
所有位均變為有效位.(dp[i] = i)
為什麼把dp[0]初始化為0恰好可以產生正確的結果呢?類似的問題最近刷題已經很常見到了.
就這題分析,顯然地,當某一時刻dp所有位均為有效值時,之後所有時刻的值均為有效值並可求出答案.
那麼只需要使得這一時刻儘早到來,觀察這題的迴圈:
#include <algorithm> #include <cstdio> #include <cstring> #include <iostream> using namespace std; int s[20], m, n, dp[100010]; int main() { cin >> m; for (int i = 1; i <= 18; i++) s[i] = i * i * i * i; for(int i = 1; i <= m; i++) dp[i] = 100000000; // INF // dp[1] = 1; for (int i = 1; i <= 18; i++) for(int j = s[i]; j <= m; j++) dp[j] = min(dp[j], dp[j - s[i]] + 1); cout << dp[m] << endl; return 0; }
i = 1,j 從s[i](即1)直到m,首先可知dp[1] = min(INF, dp[0] + 1),此後,dp[j + 1] = min(INF, dp[j] + 1).
i = 1這一輪迴圈後不再存在INF,即dp所有位均為有效值.
想出構造的方法導向這個狀態即可,不需要過分考慮內在的邏輯性(本題 "0" 似乎不能由 0 個數的四次方表示).
再結合本題,可以猜測涉及到取min時可以初始化dp為INF來處理無效位.類似地,涉及到取max時可以考慮初始化dp為0來應對.
你不覺得AC程式碼已經貼在上面了嗎?View Code