1. 程式人生 > 實用技巧 >P1679 神奇的四次方數

P1679 神奇的四次方數

P1679 神奇的四次方數

取還是不取,取的話取多少,完全揹包.

注意初始化的問題.

完全揹包問題,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