亚洲免费在线-亚洲免费在线播放-亚洲免费在线观看-亚洲免费在线观看视频-亚洲免费在线看-亚洲免费在线视频

DP-母函數(shù)

系統(tǒng) 1918 0

DP---母函數(shù)

先由錢幣兌換問題開始 http://acm.hdu.edu.cn/showproblem.php?pid=1284

Problem Description

在一個(gè)國家僅有1分,2分,3分硬幣,將錢N兌換成硬幣有很多種兌法。請你編程序計(jì)算出共有多少種兌法。

Input

每行只有一個(gè)正整數(shù)N,N小于32768。

Output

對應(yīng)每個(gè)輸入,輸出兌換方法數(shù)。

這道題有三種解法(參照此博客http://www.cnblogs.com/Findxiaoxun/p/3574907.html)

完全背包的解法很容易想到,模板性質(zhì)的。

第二種技巧性的就會(huì)強(qiáng)一點(diǎn)。這樣想,先只考慮1和3,不是1,就是3,全1的只有一種;每三個(gè)1就可以兌換一個(gè)3,方法數(shù)就有n/3種。再考慮2的情況,全部是2和1的情況,實(shí)際上,去掉i個(gè)3,就可以變成只含有1和2的情況,而類似的,只含有1和2的情況,可以有(n-3*i)/2種,此時(shí)只需要把這些方法數(shù)加起來就可以了。

第三種就是母函數(shù)了,在此引用下這位大神的博客(http://www.wutianqi.com/?p=596),

Woo講的很好了,尤其是算法歸納的過程,不過個(gè)人感覺少了對代碼的分析,這里我毛遂自薦,來講下我的理解: 大家在學(xué)習(xí)母函數(shù)的時(shí)候,一定要記住理解這樣一句話:母函數(shù)算法其實(shí)就是模擬手算多項(xiàng)式乘法。

首先要看Woo的那篇文章,最重要的理解是:

① 、首先對c1初始化,由第一個(gè)表達(dá)式(1+x+x^2+..x^n)初始化,把質(zhì)量從0到n的所有砝碼都初始化為1.

② 、 i從2到n遍歷,這里i就是指第i個(gè)表達(dá)式,上面給出的第二種母函數(shù)關(guān)系式里,每一個(gè)括號括起來的就是一個(gè)表達(dá)式。

③、j 從0到n遍歷,這里j就是(前面i個(gè)表達(dá)式累乘的表達(dá)式)里第j個(gè)變量,(這里感謝一下seagg朋友給我指出的錯(cuò)誤,大家可以看下留言處的討論)。如(1+x)(1+x^2)(1+x^3),j先指示的是1和x的系數(shù),i=2執(zhí)行完之后變?yōu)?(1+x+x^2+x^3)(1+x^3),這時(shí)候j應(yīng)該指示的是合并后的第一個(gè)括號的四個(gè)變量的系數(shù)。

④ 、 k表示的是第j個(gè)指數(shù),所以k每次增i(因?yàn)榈趇個(gè)表達(dá)式的增量是i)。

⑤ 、把c2的值賦給c1,而把c2初始化為0,因?yàn)閏2每次是從一個(gè)表達(dá)式中開始的。

Woo這里是根據(jù)題目來說的,而且,其實(shí)他的代碼里Num那個(gè)地方,其實(shí)是有兩種的,一般情況下。 來看下母式子: (1+x+x^2+x^3+x^4+..)(1+x^2+x^4++..)(1+x^3+x^6+..) 第一個(gè)括號指的是1分的,在拆分第一個(gè)括號的之前,1分的能夠構(gòu)成數(shù)m,那么c1[m]=1;如果是有a個(gè)1分的,那么1分一直a,c1[a]=1;然后開始拆分第一個(gè)括號,就是把第一個(gè)括號和第二個(gè)括號合并,我們手算多項(xiàng)式乘法,先按括號一的那個(gè)1,乘(第二個(gè)括號里的內(nèi)容),很自然的c2[i]+=c1[i],這里的c2[i]表示的是在這次的循環(huán)過程中,中間的結(jié)果,這句話理解不了的話,繼續(xù)看。然后括號一的第二個(gè)元素x,x*(....)=x*1+x*x^2+x*x^4...=x+x^3+x^5...最終,它們會(huì)和第一次乘出來的結(jié)果同項(xiàng)合并,x^3+x^3=2*x^3,其他值都類似,那么,c2[3]+=c1[1];c1[i]就是保存的之前乘出來的x^i結(jié)果的系數(shù),等這一次乘完,c2[i]保存了最終的結(jié)果,那就把它的值都轉(zhuǎn)移到c1里面去,然后c2又空。繼續(xù)分解括號。

舉個(gè)例子:

3個(gè)1分的硬幣,1個(gè)2分的,2個(gè)3分的。

(1+x+x^2+x^3)(1+x^2)(1+x^3+x^6)

1.初始化:

  0 1 2 3 4 5 6 7 8 9

c1:?1 1 1 1 0 0 0 0 0 0

C2全0

2.對第一個(gè)括號的1*(1+x^2),(i=2,j=0)得到  

   0 1 2 3 4 5 6 7 8 9

C2: 1 0 1 0 0 0 0 0 0 0

就是說,原來2個(gè)1可以組成2,現(xiàn)在,一個(gè)2就能替換,有1種方法。

3. X*(1+x^2) (i=2;j=1)  

   0 1 2 3 4 5 6 7 8 9

C2: 1 1 1 1 0 0 0 0 0 0

4. X^2*(1+x^2) j=2   

   0 1 2 3 4 5 6 7 8 9

C2: 1 1 2 1 0 0 0 0 0 0

5. x^3*(1+x^2) j=3

  0 1 2 3 4 5 6 7 8 9

C2:1 1 2 2 0 0 0 0 0 0

把c2的值全都轉(zhuǎn)移到c1里 i=3

剩下的步驟也是如此。

就是模擬手工計(jì)算多項(xiàng)式乘法。

Woo的博客介紹的幾道題都挺不錯(cuò),現(xiàn)在外面來把這個(gè)算法真正的理解運(yùn)用下:

HDU1711,題意就是說,給價(jià)值不同的一些物品,讓你把它們分成和盡可能接近的兩堆。

一種很巧妙的思路是,轉(zhuǎn)換成完全背包,或者是01背包。因?yàn)閯?dòng)態(tài)規(guī)劃解決的問題一般是最××的問題,最多,最少,最接近,等等。而這個(gè)題,可以看成是sum/2空間的背包,讓你用那些物品裝,盡可能裝滿。背包的代碼我先附上,背包專輯我后續(xù)我會(huì)整理出來。

        #include<cstdio>
        
          

#include
        
        <algorithm>
        
          

#include
        
        <cstring>


        
          using
        
        
          namespace
        
        
           std;


        
        
          const
        
        
          int
        
         maxn=
        
          5005
        
        
          ;


        
        
          const
        
        
          int
        
         maxm=
        
          255555
        
        
          ;


        
        
          int
        
        
           n,t,sum;


        
        
          int
        
        
           dp[maxm],v[maxn];




        
        
          int
        
        
           main(){

    
        
        
          while
        
        (scanf(
        
          "
        
        
          %d
        
        
          "
        
        ,&t)&&t>
        
          0
        
        
          ){

        
        
        
          int
        
        
           a,b;

        n
        
        =
        
          0
        
        ;sum=
        
          0
        
        
          ;

        memset(v,
        
        
          0
        
        ,
        
          sizeof
        
        
          (v));

        
        
        
          for
        
        (
        
          int
        
         i=
        
          0
        
        ;i<t;i++
        
          ){

            scanf(
        
        
          "
        
        
          %d%d
        
        
          "
        
        ,&a,&
        
          b);

            
        
        
          while
        
        (b--
        
          ){

                v[n
        
        ++]=a;
        
          //
        
        
          wa
        
        

                sum+=
        
          a;

            }

        }

        
        
        
          int
        
         sum2=sum/
        
          2
        
        
          ;

        memset(dp,
        
        
          0
        
        ,
        
          sizeof
        
        
          (dp));

        
        
        
          for
        
        (
        
          int
        
         i=
        
          0
        
        ;i<n;i++
        
          ){

            
        
        
          for
        
        (
        
          int
        
         j=sum2;j>=v[i];j--
        
          ){

                dp[j]
        
        =max(dp[j],dp[j-v[i]]+
        
          v[i]);

            }

        }


        
        
          //
        
        
                  for(int i=0;i<=sum2;i++)printf("%d  ",dp[i]);
        
        
          int
        
         ans=max(dp[sum2],sum-
        
          dp[sum2]);

        printf(
        
        
          "
        
        
          %d %d\n
        
        
          "
        
        ,ans,sum-
        
          ans);



    }



    
        
        
          return
        
        
          0
        
        
          ;

}
        
      
View Code

?

再來看母函數(shù)的思路。可以這樣想,因?yàn)轭}中對價(jià)值的限制為50,也就是說,只有50種值,有50種面值個(gè)數(shù)不同的硬幣,要你求出,能表示出的最接近總數(shù)一半的那個(gè)值。轉(zhuǎn)換為了母函數(shù)問題。來看i,j,k 50種面值,1的可以直接先處理,那么i=2 to 50,而j的范圍就是0 to sum,k則是(k=0;k+j<=sum&& k<i*num[i];k+=i)k的限制也很好理解,因?yàn)橹挥衝um[i]個(gè)i面值的硬幣,則多項(xiàng)式最多就能乘到x^(i*num[i])。

        #include<cstdio>
        
          

#include
        
        <cstring>
        
          

#include
        
        <algorithm>


        
          using
        
        
          namespace
        
        
           std;




        
        
          int
        
         c1[
        
          250005
        
        ],c2[
        
          250005
        
        
          ];


        
        
          int
        
         data[
        
          51
        
        
          ];


        
        
          int
        
        
           sum;


        
        
          int
        
        
           main(){

    
        
        
          int
        
        
           n;

    
        
        
          while
        
        (scanf(
        
          "
        
        
          %d
        
        
          "
        
        ,&n)&&n>
        
          0
        
        
          ){

        sum
        
        =
        
          0
        
        
          ;

        
        
        
          int
        
        
           a,b;

        memset(data,
        
        
          0
        
        ,
        
          sizeof
        
        
          (data));

        memset(c1,
        
        
          0
        
        ,
        
          sizeof
        
        
          (c1));

        memset(c2,
        
        
          0
        
        ,
        
          sizeof
        
        
          (c2));

        
        
        
          for
        
        (
        
          int
        
         i=
        
          0
        
        ;i<n;i++
        
          ){

            scanf(
        
        
          "
        
        
          %d%d
        
        
          "
        
        ,&a,&
        
          b);

            data[a]
        
        =
        
          b;

            sum
        
        +=a*
        
          b;

        }

        
        
        
          for
        
        (
        
          int
        
         i=
        
          0
        
        ;i<=data[
        
          1
        
        ];i++)c1[i]=
        
          1
        
        
          ;

        
        
        
          for
        
        (
        
          int
        
         i=
        
          2
        
        ;i<=
        
          50
        
        ;i++
        
          ){

            
        
        
          for
        
        (
        
          int
        
         j=
        
          0
        
        ;j<=sum;j++
        
          ){

                
        
        
          for
        
        (
        
          int
        
         k=
        
          0
        
        ;j+k<=sum&&k<=i*data[i];k+=
        
          i)

                    c2[j
        
        +k]+=
        
          c1[j];

            }

            
        
        
          for
        
        (
        
          int
        
         j=
        
          0
        
        ;j<=sum;j++
        
          ){

                c1[j]
        
        =c2[j];c2[j]=
        
          0
        
        
          ;

            }

        }

        
        
        
          int
        
         x=sum/
        
          2
        
        
          ;

        
        
        
          while
        
        (!c1[x]){x--
        
          ;}

        
        
        
          int
        
         ans=max(x,sum-
        
          x);

        printf(
        
        
          "
        
        
          %d %d\n
        
        
          "
        
        ,ans,sum-
        
          ans);

    }







    
        
        
          return
        
        
          0
        
        
          ;

}
        
      
View Code

?

DP-母函數(shù)


更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主

微信掃碼或搜索:z360901061

微信掃一掃加我為好友

QQ號聯(lián)系: 360901061

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。

【本文對您有幫助就好】

您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描上面二維碼支持博主2元、5元、10元、自定義金額等您想捐的金額吧,站長會(huì)非常 感謝您的哦!!!

發(fā)表我的評論
最新評論 總共0條評論
主站蜘蛛池模板: 九九热视频免费观看 | 福利国产在线 | 国产成人青青热久免费精品 | 日日摸夜夜添夜夜添影院视频 | a视频在线看 | 久久综合给合久久狠狠狠97色 | 久久精品亚瑟全部免费观看 | 久久91精品久久91综合 | 国产精品久久久久久 | 高清一区二区亚洲欧美日韩 | 免费看欧美毛片大片免费看 | 国产亚洲精品成人一区看片 | 久久www香蕉免费人成 | 久久久久久久久综合 | 久草网在线| 瑟瑟综合 | 精品视频在线视频 | 天天综合色天天综合网 | 伊人75| 国产色综合久久无码有码 | 欧美一级毛片欧美毛片视频 | 亚洲国产一区在线二区三区 | 国产福利在线观看永久视频 | 不卡网 | 亚洲区中文字幕 | 99精品视频在线观看免费专区 | 九九热在线视频播放 | 天天做天天爱天天一爽一毛片 | 午夜激情男女 | 久久美女免费视频 | 精品国产免费久久久久久婷婷 | 97影院理论在线观看 | 欧美亚洲精品一区 | 四虎海外影库www4hu | 国内精品伊人久久大香线焦 | 午夜体验 | 国产婷婷色一区二区三区 | 天天操天天摸天天干 | 中国女人精69xxx | 国产精品久久久久久久成人午夜 | 91av最新地址 |