1. 程式人生 > >母函式(整數劃分)

母函式(整數劃分)

                                 母函式的分析

    母函式的原理數,後生之輩沒啥可講的。完全可以站在巨人的肩膀上。下面給出杭電科技大學的課程連結,不懂得就自己去獲取基礎知識點吧。Click Here~

    個人學了一些有關母函式的知識,經過本人的總結。個人覺得大體上解體策略可以分為動態規劃及遞迴型和母函式求解型。而其中動態規劃及遞迴的主要是運用與有關整數劃分的問題。而整數劃分又分很多種情況。下面給出哈工大學大牛寫的部落格連結自己可以去看看Click Here~,而母函式的問題就是上面給出的杭大的連結。下面就給出每種情況的問題詳解吧。

   而使用模板的時候還要注意題目的限定條件。如果,題目是要求把一個數或物品必須分成k份,則此時就要用到dp思想的運用。即,dp[k][n] = dp[k][n-k] + dp[k-1][n-1] (n>=k)。而當題目中說明可以把一個數或者物品分成任意的份數,則此事可以用到遞迴的模板。

整數劃分模板:

typedef long long LL;
LL GetPartitation(int n,int m) //n表示要劃分的整數,m表示序列中最大不大於m
{
    if(n==0||m==0||n==1||m==1) //根據題目變化,自己靈活設定
      return 1;
    else if(n < m)
      return GetPartitation(n,n);
    else if(n == m)
      return GetPartitation(n,n-1) + 1;
    else
      return GetPartitation(n,m-1) + GetPartitation(n-m,m);
}

   我在根據本人自己對上面模板的理解告訴大家吧!我感覺這種方式比網上解釋的更容易記憶。

          if(n==0||m==0||n==1||m==1) //根據題目變化,自己靈活設定     

              return 1; 

         else if(n < m)     //這個是顯然的序列中最大的,肯定是不可能大於n的

             return GetPartitation(n,n);   

        else if(n == m)   //當序列中最大數是n==m時候,顯然此時只可能只有一個n,然後遞迴比m小下一個數

            return GetPartitation(n,n-1) + 1;  

        else //其中前部分是表示遞迴比m小的下一個數,後面一部份是表示可以與m組合成n的組合數有幾個    

             return GetPartitation(n,m-1) + GetPartitation(n-m,m);

動態規劃DP模版:

for(int i = 1;i < N;++i)  //初始化
   dp[1][i] = dp[i][i] = dp[0][i] = 1;
for(int i = 2;i < N;++i)  
  for(int j = i+1;j < N;++j){  //自己看上面哈工大連結的解釋
     dp[i][j] = dp[i-1][j-1] + dp[i][j-i];
  }

整數的劃分在北大上有兩題簡單的入門題,

一題是放蘋果題(運用上面的遞迴模板就可以解決)

二是Click Here~(是動態規劃思想解決的問題)是整數劃分中的第二類,即劃分n是數量固定的k個數。

轉移方程式:

dp[k][n] = dp[k][n-k] + dp[k-1][n-1];


母函式的模板:

流行版

for(i = 0;i <= n;++i){
    c1[i] = 1; c2[i] = 0;
} 
for(i = 2;i <= n;++i){      //有多少個可以組合的資料種類
   for(j = 0;j <= P;++j){   //前一個括號中有多少項
      for(k = 0;k+j <= P;k += i){  //當前的括號數的指數最大可以達到的數值
         c2[k+j] += c1[j];
      }        
    }
    for(j = 0;j <= n;++j){  
       c1[j] = c2[j];  
       c2[j] = 0; 
    }
}
/* P為資料中可能的最大指數
   k+j<=P最大指數不超P
*/

改版版:

a[MAX],b[MAX];     //a為結果,b為中間結果
memset(a,0,sizeof(a));
a[0] = 1;
for(i = 1;i <= N;++i){
  memset(b,0,sizeof(b));
  for(j = min[i];(j<=max[i])&&j*v[i]<=P;++j){ //迴圈每個因子裡的每一項
     for(k = 0;k+j*v[i]<=P;++k){              //迴圈a的每一個項
        b[k+j] += a[k];
     }
  }
  memcpy(a,b,sizeof(b));                   //b賦值給a
}

/*一、P是可能的最大指數。
  二、如果max是無窮,那麼第二層迴圈條件j <= max[i]可以省掉
*/
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 300+5;
int c1[N],c2[N];
int main()
{
    int n,i,j,k;
    while(cin>>n,n)
    {
        for(i = 0;i <= n;++i){
           c1[i] = 1;
           c2[i] = 0;
        }
        for(i = 2;i <= 17;++i){   //這裡改變了一點  
            for(j = 0;j <= n;++j){   
               for(k = 0;k+j <= n;k += i*i){  //這裡要根據題目要求變
                  c2[k+j] += c1[j];
               }
            }
            for(j = 0;j <= n;++j){
               c1[j] = c2[j];
               c2[j] = 0;
            }
        }
        cout<<c1[n]<<endl;
    }
    return 0;
}

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 1e6 + 5;
int c1[N],c2[N];
int main()
{
    int n,m,k;
    while(scanf("%d%d%d",&n,&m,&k),(n||m||k))
    {
        int sum = n + 2*m + 5*k;
        memset(c1,0,sizeof(c1));
        memset(c2,0,sizeof(c2));
        for(int i = 0;i <= n;++i){ //1 coin
          c1[i] = 1;
        }
        for(int i = 0;i <= n;++i){   //2 coin
          for(int j = 0;j <= 2*m;j += 2){
             c2[i+j] += c1[i];
          }
        }
        for(int i = 0;i <= sum;++i){
           c1[i] = c2[i];
           c2[i] = 0;
        }
        for(int i = 0;i <= n+2*m;++i){ //5 coin
          for(int j = 0;j <= 5*k;j += 5){
             c2[i+j] += c1[i];
          }
        }
        for(int i = 0;i <= sum;++i){
           c1[i] = c2[i];
           c2[i] = 0;
        }
        int i;
        for(i = 0;i <= sum;++i){
           if(!c1[i]){
              printf("%d\n",i);
              break;
           }
        }
        if(i == sum+1){
           printf("%d\n",sum+1);
        }
    }
    return 0;
}

題意分析:

      給你N個不同價值,不同數量的東西。叫你找出使這些物品可以達到最大的平均值。

思路分析:

      原先我開始做的時候是用了一個結構體儲存資料,然後給價值排序,但是後來TEL了。後來我看了別人的程式碼發現都不用排序。不知道為什麼。其實,這道題可以用揹包來做,我很早就見過,但是當時沒做出來,最近在學母函式,於是就把它A了。就是一個簡單的母函式模板題。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 1e6 + 5;
int c1[N],c2[N],val[N],num[N];
int main()
{
    int n,i,j,k;
    while(scanf("%d",&n),(n>=0))
    {
//        int sum = 0;
        for(i = 1;i <= n;++i){
           scanf("%d%d",&val[i],&num[i]);
//           sum += val[i]*num[i];
        }
        memset(c1,0,sizeof(c1));
        memset(c2,0,sizeof(c2));
        for(i = 0;i <= val[1]*num[1];i += val[1]){
           c1[i] = 1;
        }
        int m = val[1]*num[1];
        for(i = 2;i <= n;++i){
          for(j = 0;j <= m;++j){
             for(k = 0;k <= val[i]*num[i];k += val[i]){
                c2[k+j] += c1[j];
             }
          }
          m += val[i]*num[i];
          for(j = 0;j <= m;++j){
             c1[j] = c2[j];
             c2[j] = 0;
          }
        }
        int mid = m/2;
        while(!c1[mid]) mid++;
        int two = m - mid;
        if(two > mid) swap(two,mid);
        printf("%d %d\n",mid,two);
    }
    return 0;
}
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 1e3 + 5;
int c1[N],c2[N],n1[N],n2[N];
int main()
{
    int n,m,i,j,k;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        for(i = 0;i < n;++i)
          scanf("%d%d",&n1[i],&n2[i]);
        memset(c1,0,sizeof(c1));
        for(i = n1[0];i <= n2[0];++i){
           c1[i] = 1;
        }
        for(i = 1;i < n;++i){
          memset(c2,0,sizeof(c2));
          for(j = 0;j <= m;++j){
             for(k = n1[i];k <= n2[i];++k){
                c2[j+k] += c1[j];
             }
          }
          memcpy(c1,c2,sizeof(c2));
        }
        printf("%d\n",c1[m]);
    }
    return 0;
}

題目分析:

     給你字母數量從A ~ B的個數,要求你求出在這些給出的字母個數中可以得到的組合數的總和不小於50的個數。昨天,就看到這題了,一開始居然沒想明白,好吧,我承認,我SB了。經過多次的看題後才發現了,就是一個母函式的模板題。

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
using namespace std;

const int N = 100;
const int P = 50;
int c1[N],c2[N],a[N];
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        memset(c1,0,sizeof(c1));
        for(int i= 1;i <= 26;i++)
          scanf("%d",&a[i]);
        for(int i = 0;i <= a[1];++i){
           c1[i] = 1;
        }
        for(int i = 2;i <= 26;++i){
           memset(c2,0,sizeof(c2));
           for(int j = 0;j <= P;++j){
             for(int k = 0;(k+j<=P)&&(k <= a[i]*i);k += i){
                c2[k+j] += c1[j];
             }
           }
           memcpy(c1,c2,sizeof(c2));
        }
        int ans = 0;
        for(int i = 1;i <= P;++i){
           ans += c1[i];
        }
        printf("%d\n",ans);
    }
    return 0;
}

相關推薦

函式整數劃分

                                 母函式的分析     母函式的原理數,後生之輩沒啥可講的。完全可以站在巨人的肩膀上。下面給出杭電科技大學的課程連結,不懂得就自己去獲

sincerit 函式組合問題

大佬程式碼: https://blog.csdn.net/yu121380/article/details/79914529 https://blog.csdn.net/xiaofei_it/article/details/17042651?utm_source=blogxgwz0 有1克、

hdu2082找單詞解題報告---函式組合數學

找單詞 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 10817  

基本演算法整數劃分

#include <stdio.h> int main() {  int s, i, j, k, t, u;  static int a[21][800][21];  printf("input s(s<=20):"); scanf_s("%d", &s);  a[2][

函式指數型泰勒展開式

/*http://blog.sina.com.cn/s/blog_79b832820100x8pa.html HDU 2065 "紅色病毒"問題 (泰勒級數推導) */ #include <i

hdu2079選課時間解題報告---函式/生成函式組合數學

                                          &

卡特蘭數通項公式函式,牛頓展開

組合意義非常顯然,經典的路徑問題。這裡主要討論母函式以及牛頓展開的證明。 考慮卡特蘭數的遞推式,發現這是一個卷積式 令 f (

求表示方法整數劃分問題

設 m、n 均為自然數,m 可表示為一些不超過 n 的自然數之和,f(m,n) 為這種表示方式的數目。 例如,f(5,3)=5,有5種表示方法:3+2,3+1+1,2+2+1,2+1+1+1,1+1+1+1+1。 請編寫程式,計算f(m,n)的值。 輸入:           m n 輸出: 

區間dp模型石子歸併,括號匹配,整數劃分

區間dp顧名思義就是在一個區間上進行的一系列動態規劃。對一些經典的區間dp總結在這裡。 1) 石子歸併問題 描述:有N堆石子排成一排,每堆石子有一定的數量。現要將N堆石子併成為一堆。合併的過程只能每次將相鄰的兩堆石子堆成一堆,每次合併花費的代價為這兩堆石子的和,經過

區間dp整數劃分,石子劃分

整數劃分(四) 基礎區間dp,程式碼: #include <cstdio> #include <cstring> long long a[20][20]; long lon

vijos 1117 數的劃分整數拆分

將整數n分成k份,且每份不能為空,任意兩份不能相同(不考慮順序)。例如:n=7,k=3,下面三種分法被認為是相同的。1,1,5; 1,5,1; 5,1,1;問有多少種不同的分法。dp:剩下的j分i份,最大的那份為k#include<bits/stdc++.h> u

函式生成函式

此文章為轉載,此為原連結:點選開啟連結 母函式又稱生成函式。定義是給出序列:a0,a1,a2,.......ak,......,那麼函式G(x)=a0+a1*x+a2*x2+......ak*xk稱為序列a0,a1,a2,.......ak,......的母函式(

HDU 1028 Ignatius and the Princess IIIDP,整數劃分

Ignatius and the Princess III Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 2

hdu 1709 函式,有些特殊

母函式。。 #include"stdio.h" #include"string.h" int main() { int a[10008]; int b[10008]; in

【練習題】第十六章--類和函式Think Python

class Time: hour=0 minute=0 second=0 def print_time(t): print("%.2d:%.2d:%.2d"%(t.hour,t.minute,t.second)) def is_after(t1,t2):

【練習題】第三章--函式Think Python

讀書筆記: 1.函式定義的第一行叫做頭部,剩下的叫做函式體。函式頭部的末尾必須有一個冒號,函式體必須是相對函式頭部有縮排的,距離行首相對於函式頭要有四個空格的距離。函式體可以有任意長度的語句。 2.在函式定義完畢的結尾,必須輸入一行空白行。定義函式會建立一個函式類的物件,有type函式。

【練習題】第六章--有返回值的函式Think Python

增量式開發(incremental development): 這個過程的核心如下: 一定要用一個能工作的程式來開始,每次逐漸新增一些細小增補。在任何時候遇到錯誤,都應該弄明白錯誤的位置。 用一些變數來儲存中間值,這樣你可以顯示一下這些值,來檢查一下。 程式一旦能

型別函式type function

在C和C++中,我們常見的是值函式(value function),即函式接收的引數是某些值,而且函式的返回結果也是值。 至於型別函式(type function),即函式接收某些型別的實參,並生成一個型別作為函式的返回結果。 例子: 1、內建函式sizeof就是一個型別函式,它返回

高斯函式Gaussian function的詳細分析

摘要     論文中遇到很重要的一個元素就是高斯核函式,但是必須要分析出高斯函式的各種潛在屬性,本文首先參考相關材料給出高斯核函式的基礎,然後使用matlab自動儲存不同引數下的高斯核函式的變化gif動圖,同時分享出原始碼,這樣也便於後續的論文寫作。 高斯函式的基礎

C/C++ 演算法分析與設計:貪心整數配對

題目描述 江鳥想到一個有趣的問題:給你N個正整數,你可以將這N個整數按兩個一組的方式成對劃分,當然其中的元素也可以不和其他元素配對劃分。現在的問題是,讓劃分為一對的元素的乘積與未配對的元素相加求和,並且讓和最大。比如:考慮這個集合{0,1,2,4,5,3},如果我們讓{0,3}、{2,5}分別成