2016級演算法第三次上機-C.AlvinZH的奇幻猜想——三次方

來源:互聯網
上載者:User

標籤:貪心   eof   ++i   封裝   mission   lin   miss   越界   數值   

905 AlvinZH的奇幻猜想——三次方思路

中等題。題意簡單,題目說得簡單,把一個數分成多個立方數的和,問最小立方數個數。

腦子轉得快的馬上想到貪心,從最近的三次方數往下減,反正有1^3在最後撐著保證減完。不好意思這是錯的,因為1,27,64,125...等立方數之間並不是倍數關係,不能構成貪心策略。舉個反例:96=64+8+8+8+8=64+27+1+1+1+1+1,答案明顯是5,而貪心會算到7。

既然不是貪心,那就是DP了,沒毛病。先講一下常規做法吧,是這樣想的:相當於把一個數化成幾份,求最小劃分的份數,那麼把所求數看成背包的總體積,每個立方數的值看作每個物品的體積,價值都看做1,問題就轉化為完全背包(因為每個立方數可以取多次)恰好裝滿求最小的價值,仔細想想。

所以,套一下完全背包的板子吧,但是,這裡的一個問題是完全背封裝滿,這怎麼辦呢(可能由於本題可以保證裝滿這個問題不怎麼顯眼)。舉一反三,假設有可能裝不滿,怎麼辦?這個問題初始化dp數組時就可以解決。以下方法對於01背包同樣適用:

  • 普通01背包or完全背包:初始化為0;
  • 01背包or完全背封裝滿求價值最小:初始化為一個大數值如 \(INF\) (0x3f3f3f3f);
  • 01背包or完全背封裝滿求價值最大:初始化為一個小數值如 \(-INF\) (-0x3f3f3f3f);

為什麼呢?對於本題,初始化為大數值,如果沒裝滿,最後dp[n]會依然是INF,因為我們每次比較取的都是較小值。第二次練習賽的G題就是01背封裝滿求最大價值,初始化為最小值即可。(熬夜寫了這麼多,希望大家看得到QAQ)

這題還有另外的解法,那就是類似打表,先把所有數的最小立方個數通過迭代計算得到,然後 \(O(1)\) 時間取得答案。具體可見隊列迭代的參考代碼二以及for迴圈迭代的參考代碼三。其實這裡面也是有DP的思想,因為每次迭代會用到之前的結果,對於多組資料來講,同樣可以節省時間。簡單易懂,可以學習一下。

分析

對於完全背包直接解法,時間複雜度為 \(O(V*∑(V/wi))\) 。

迭代的話複雜度差不了多少,由於多組資料的原因,迭代打表運行總時間顯得更短些,不糾結這個。

擴充:完全背封裝滿問題:HDU 1114。

參考代碼一:完全背封裝滿求最小价值
//// Created by AlvinZH on 2017/10/24.// Copyright (c) AlvinZH. All rights reserved.//#include <cstdio>#include <cstring>#define INF 0x3f3f3f3f#define MaxSize 1000005int weight[105];int ans[MaxSize];int main(){    for (int i = 1; i < 105; ++i)        weight[i] = i * i * i;    int n;    while(~scanf("%d", &n))    {        for (int i = 0; i <= n; ++i)//初始化為最大值            ans[i] = INF;        ans[0] = 0;        for (int i = 0; i < 105; ++i) {            for (int j = weight[i]; j <= n; ++j) {                if(ans[j] > ans[j-weight[i]] + 1)                    ans[j] = ans[j-weight[i]] + 1;            }        }        if(ans[n] == n) printf("Oh NO!\n");        else printf("%d\n", ans[n]);    }}/* * 完全背包恰好裝滿問題,求最小值。 */
參考代碼二:隊列迭代
//// Created by AlvinZH on 2017/10/24.// Copyright (c) AlvinZH. All rights reserved.//#include <cstdio>#include <cstring>#include <queue>#define INF 0x3f3f3f3f#define MaxSize 1000005using namespace std;int weight[105];int ans[MaxSize];queue<int> Q;void init(){    memset(ans, INF, sizeof(ans));    for (int i = 1; i < 105; ++i)        weight[i] = i * i * i;    for (int i = 1; i <= 100; ++i) {        ans[weight[i]] = 1;        Q.push(weight[i]);    }    while(!Q.empty())    {        int w = Q.front();        Q.pop();        for (int i = 1; i <= 100; ++i) {            int num = weight[i] + w;            if(num > 1000000) break;            if(ans[num] < INF) continue;            ans[num] = ans[w] + 1;            Q.push(num);        }    }}int main(){    init();    int n;    while(~scanf("%d", &n))    {        if(ans[n] == n) printf("Oh NO!\n");        else printf("%d\n", ans[n]);    }}/* * 思路:直接寬搜,把最開始的數扔進隊列,反覆用隊列中的數去更新沒更新的數就行了,注意數組別越界。 */
參考代碼三:for迴圈迭代
/* Author: 曾宥崴(13422) Result: AC Submission_id: 391756 Created at: Fri Nov 10 2017 18:26:40 GMT+0800 (CST) Problem: 905   Time: 353   Memory: 6612*/#include <cstdio>#include <algorithm>#include <iostream>using namespace std;int f[1000001],n;int main(){    for (int i = 1; i <= 1000000; i++) f[i] = 1000000000;    f[0] = 0;    for (int i = 0; i <= 1000000; i++)        for (int k = 1; i + k * k * k <= 1000000; k++)            f[i+k*k*k] = min(f[i+k*k*k],f[i] + 1);    while (scanf("%d",&n) != EOF)        if (f[n] == n) printf("Oh NO!\n");        else printf("%d\n",f[n]);    return 0;}

2016級演算法第三次上機-C.AlvinZH的奇幻猜想——三次方

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.