1. 程式人生 > >P1679 神奇的四次方數

P1679 神奇的四次方數

什麽 記憶化 tro 初始化 fun pac 超過 -h str

P1679 神奇的四次方數

題目描述

在你的幫助下,v神終於幫同學找到了最合適的大學,接下來就要通知同學了。在班級裏負責聯絡網的是dm同學,於是v神便找到了dm同學,可dm同學正在忙於研究一道有趣的數學題,為了請dm出山,v神只好請你幫忙解決這道題了。

題目描述:將一個整數m分解為n個四次方數的和的形式,要求n最小。例如,m=706,706=5^4+3^4,則n=2。

輸入輸出格式

輸入格式:

一行,一個整數m。

輸出格式:

一行,一個整數n。

輸入輸出樣例

輸入樣例#1: 復制
706
輸出樣例#1: 復制
2

說明

數據範圍:對於30%的數據,m<=5000;對於100%的數據,m<=100,000

由記憶化遞歸的參數推出DP的狀態及狀態轉移方程

完全背包問題

洛谷題解:

這道題目我們使用背包問題的思想來做。

這裏,我們先把每一個四次方數打表。打到什麽位置呢?

通過簡單的推理,我們發現,只要打到\sqrt[4]{m}4m?的4次方就夠了。

為什麽呢?因為為了湊出這個數,我們肯定用比他小的數來湊,如果超過了\sqrt[4]{m}4m?,就不可能用上了。

因此我們也可以用樓下的方法,打表打到18,因為{18}^{4}184已經超過了max(m)={10}^{5}max(m)=105。

然後,以每一個四次方數為體積,1為物品重量,做完全背包(壓維)。

特殊在於,f數組初始化為Inf.初值設置f_0=0f0?=0,那麽最終的結論就是f_mfm?

代碼:

(其實這篇題解最重要的是下面這個部分)。

Extra:若把四方數改為平方數,那麽做法也是一樣的;

不過這時有一個定理,叫拉格朗日四方和定理。有興趣的同學可以自行查閱。

它的大概意思就是,任何自然數都可以表示為n個平方數之和(n<=4)。

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<cmath>
 4 #define min(x,y) (x<y?x:y)
 5 const int MAXN=200010;
 6 int s[MAXN],f[MAXN];
 7 int m;
 8 
 9
int main() 10 { 11 scanf("%d",&m); 12 for(int i=1;i<=m;i++) 13 f[i]=1e8; 14 int n=ceil(sqrt(sqrt(m))+1); 15 for(int i=1;i<=n;i++) 16 s[i]=i*i*i*i; 17 for(int i=1;i<=n;i++) 18 for(int j=s[i];j<=m;j++) 19 f[j]=min(f[j],f[j-s[i]]+1); 20 printf("%d\n",f[m]); 21 }

這道題……比較容易看出來是個完全背包問題~

只不過要我們求最小值

遞推關系f[v]=max{f[v],f[v-w[i]+c[i]]}

這裏我們讓i=1 to 18 (18^4>100000)

w[i]=i*i*i*i;c[i]=1;這裏我們可以省略掉c[i]數組了

我們只需要將f[i]初始化為inf並且f[0]=0;

還有一些微小的改動

具體參考代碼:

#include<bits/stdc++.h>
int f[200001],w[200001],n=18,m;
using namespace std;                    //全局變量部分
int main()
{
    memset(f,0xf,sizeof(f));          
    f[0]=0;cin>>m;                      //初始化數據
    for(int i=1;i<=n;i++)
        w[i]=i*i*i*i;
    for(int i=1;i<=n;i++)                    //完全背包
        for(int v=w[i];v<=m;++v)
        if(f[v]>f[v-w[i]]+1)
            f[v]=f[v-w[i]]+1;
    cout<<f[m];
}

P1679 神奇的四次方數