母函數的一些簡單總結

母函數的簡單總結

 牛人的博客地址
      :http://blog.csdn.net/hhq420684/article/details/12876993
http://blog.csdn.net/YDYKL/article/details/6655142
http://blog.csdn.net/leonharetd/article/details/8922262
http://blog.csdn.net/lac001001/article/details/45137681
http://blog.csdn.net/MetalSeed/article/details/8046656
http://blog.csdn.net/zhongshijunacm/article/details/17481181


    做題時碰到了母函數,便簡單的看了一下,並沒有系統的學過組合數學,只是看了幾位大牛的解釋,才勉強明白了一點母函數。摘抄了一些定義:
1,把組合問題的加法法則和冪級數的乘冪對應起來
2,把離散數列和冪級數一一對應起來,把離散數列見的相互結合關係對應成爲冪級數間的運算關係,最後由冪級數形式來確定離散數列的構造


一:在談論母函數問題之前,我們先看一個簡單的問題描述:假如有兩組數據(A,B)和(C,D),每組中選出一個構成一個組合,總共有幾種選法?很顯然總共有4種選法:AC,AD,BC,BD。而且很容易聯想到這個式子(A+B)*(C+D)=A*C+A*D+B*C+B*D。式子中的幾個乘積項就是上面的4種選法。假如把問題換一下:每組中選出一個或0個數據構成組合,總共有幾種組合?那麼結果就變成:{空},A,B,C,D,AC,AD,BC,BD,而式子(1+A+B)*(1+C+D)=1+C+D+A+A*C+A*D+B+B*C+B*D,正好和上面組合的結果又一致(1代表什麼都沒選)。從這2個例子我們可以發現多項式乘積和組合存在着某種關係。事實上我們可以這麼理解:(1+A+B)可以理解爲從第一組數據中取0個數據,取A或者取B,同樣(1+C+D)可以理解爲從第二組數據取0個數據,取C或者取D。兩者相乘的結果就表示了所有的組合。再看一下這個多項式:

 (1+x)*(1+x+x2)*(1+x3)=1+2x+2x2+2x3+2x4+2x5+x6

這個多項式和上面的有一些區別了,它的冪級數超過1了。如果要從(1+x)、(1+x+x2)和(1+x3)中得到x的2次方的話,有兩種選擇:從(1+x)和(1+x+x2)中分別選擇一個x或者從(1+x+x2)中選擇x2;如果要得到x的6次方的話,只有1種選擇,就是從(1+x)中選擇x、(1+x+x2)中選擇x2、(1+x3)中選擇x3。也就是說乘積結果的每一項anxn的前面的係數an表示了從(1+x)、(1+x+x2)和(1+x3)中得到xn的組合數。

其實上面的例子就利用了母函數的思想,下面來具體討論一下母函數

二:母函數就是n個多項式相乘,最終化爲G(x)=a0+a1x+a2x^2+a3x^3+a4x^4+……+anx^n的式子,相應的係數對應相應的組成方式的個數。這個就是普通型母函數


三利用普通型母函數來解決一些組合問題

1.  有1克、2克、3克、4克砝碼各一枚,問你能稱出哪幾種重量?每種重量各有幾種方案?

下面是用母函數解決這個問題的思路:

首先,我們用X表示砝碼,X的指數表示砝碼的重量。那麼,如果用函數表示每個砝碼可以稱的重量,

1個1克的砝碼可以用函數X^0 + X^1表示,x^0代表不放的狀態 x^1代表放一個1克的砝碼。

1個2克的砝碼可以用函數X^0 + X^2表示,x^0代表不放的狀態 x^2代表放一個2克的砝碼。

依次類推。

所以母函數 爲 (1 + x) * (1 + x^2 ) * (1 + x^3 ) * (1 + x^4 )

解得方程 X^0 + X^1 + X^2 + 2*X^3 + 2*X^4 + 2*X^5 + 2*X^6 + 2*X^7 + X^8 + X^9 + X^10。

因爲同底數冪相乘指數相加,所以 x^3*x^2 = x^5 說明一個3克砝碼和一個2克砝碼稱出了5克,x^1*x^4 = x^5一個1克砝碼和一個4克砝碼稱出了5克.

說明 該多項式的係數代表他解的個數 2*x^6代表稱出6克有兩種方案

2.拆分整數

以展開後的x爲例,其係數爲4,即4拆分成123之和的拆分數爲4

即 :4=1+1+1+1=1+1+2=1+3=2+2

這裏再引出兩個概念整數拆分和拆分數:

所謂整數拆分即把整數分解成若干整數的和(相當於把n個無區別的球放到n個無標誌的盒子,盒子允許空,也允許放多於一個球)。

整數拆分成若干整數的和,辦法不一,不同拆分法的總數叫做拆分數

可以構造母函數G( x ) = ( 1 + x + x^2 +····) * ( 1 + x^2 + x^4 + ····) * ( 1 + x^3 + x^6),最終x^4d的係數爲4的基本拆分總數。


四,下面我們給出網上一個基本的求母函數的模板,求母函數就是進行多項式的展開,程序實現多項式的展開和用手是實現多項式的展開是一樣的,先取第一項的每項和第二項的每項相乘,得到的結果一次和下一項相乘,最終全部展開完畢

下面是模板代碼:

<span style="font-size:18px;">#include<iostream>
using namespace std;
int main()
{
	int c1[1000];
	int c2[1000];
	int n;
	while (cin >> n)
	{
		for (int i = 0; i <= n; i++)
		{
			c1[i] = 1;//c1爲已經計算得到的結果
			c2[i] = 0;//c2爲正在計算的狀態結果
		}
		for (int i = 2; i <= n; i++)//計算第一個多項式依次和後面的多項式相乘
		{
			for (int j = 0; j <= n; j++)//從前面一個多項式中X的0——n次方係數一次計算
			{
				for (int k = 0; k + j <= n; k += i)//後面一個多項式次方數一次爲0,k+i,k+2i,x^0,x^2,x^4;x^n。因爲只計算到x^n次方,所以k+j要小於n
				{
					c2[j + k] +=1* c1[j];//元素下標即爲x的次方數.1代表第二項x^k的係數爲1.x的k次方與x的j次方相乘的值放在x^(j+k)中
				}//共有n個多項式,只計算保留x^n前的係數,因爲需要x^n的係數
			}
			for (int i = 0; i <= n; i++)//賦值給c2
			{
				c1[i] = c2[i];
				c2[i] = 0;
			}
		}
		cout << c1[n] << endl;
	}
	return 0;
}</span>
這就是基本的模板代碼

下面介紹杭電上的一些題目:

1.1.  題目:http://acm.hdu.edu.cn/showproblem.php?pid=1028

就是直接運用模板
AC代碼
<span style="font-size:18px;">#include<iostream>
using namespace std;
int main()
{
    int c1[1000];
    int c2[1000];
    int n;
    while (cin >> n)
    {
        for (int i = 0; i <= n; i++)
        {
            c1[i] = 1;
            c2[i] = 0;
        }
        for (int i = 2; i <= n; i++)//計算第一個多項式依次和後面的多項式相乘
        {
            for (int j = 0; j <= n; j++)//從前面一個多項式中X的0——n次方係數一次計算
            {
                for (int k = 0; k + j <= n; k += i)//後面一個多項式次方數一次爲0,k+i,k+2i,x^0,x^2,x^4;x^n
                {
                    c2[j + k] +=1* c1[j];//元素下標即爲x的次方數.1代表第二項x^k的係數爲1.x的k次方與x的j次方相乘的值放在x^(j+k)中
                }//共有n個多項式,只計算保留x^n前的係數,因爲需要x^n的係數
            }
            for (int i = 0; i <= n; i++)
            {
                c1[i] = c2[i];
                c2[i] = 0;
            }
        }
        cout << c1[n] << endl;
    }
    return 0;
}

</span>

2.  題目:http://acm.hdu.edu.cn/showproblem.php?pid=1398
這題基本是套模板,就是在循環條件略微改了一下
ac代碼
<span style="font-size:18px;">#include<iostream>
using namespace std;
int main()
{
//    int key[400];
    int n;
    while(cin>>n&&n)
    {
        int c1[400];
        int c2[400];
        for(int m=0;m<=n;m++)
        {
            c1[m]=1;
            c2[m]=0;
        }
       for(int i=2;i*i<=n;i++)
       {
           for(int j=0;j<=n;j++)
           {
               for(int k=0;j+k<=n;k+=i*i)//注意循環條件
               {
                   c2[j+k]+=1*c1[j];
               }
           }
           for(int v=0;v<=n;v++)
           {
               c1[v]=c2[v];
               c2[v]=0;
           }
       }
       cout<<c1[n]<<endl;
    }
    return 0;
}

</span>

3 http://acm.hdu.edu.cn/showproblem.php?pid=1085
建議這題數組開大點,這題先把所以組合情況記錄下來,找到係數爲0的指數就是無法組成的,若都可以的話,那sum+1肯定不可以,是最小了的
AC代碼
<span style="font-size:18px;">#include<iostream>
#include<string>
#include<cstring>
using namespace std;
int main()
{
	int c1[110000];
	int c2[110000];
	int a[3] = { 1, 2, 5 };
	int b[3];
	while (cin >> b[0]>> b[1]>> b[2])
	{
		if (b[0] == 0 && b[1] == 0 && b[2] == 0)
		{
			break;
		}
		memset(c1, 0, sizeof(c1));
		memset(c2, 0, sizeof(c2));
		int sum = b[0] + b[1]* 2 + b[2] * 5;
		for (int i = 0; i <= b[0]; i++)
		{
			c1[i] = 1;
		}
		for (int i = 1; i < 3; i++)
		{
			for (int j = 0; j <= sum; j++)
			{
				for (int k = 0, v = 0; v <= b[i]; k += a[i],v++)
				{
					c2[k + j] += c1[j];
				}
			}
			for (int j = 0; j <= sum; j++)
			{
				c1[j] = c2[j];
				c2[j] = 0;
			}
		}
		int i;
		for ( i = 0; i <= sum; i++)
		{
			if (c1[i] == 0)
			{
				cout << i << endl; 
				break;
			}
		}
		if (i == sum+1)
		{
			cout << sum + 1 << endl; 
		}

	}
	return 0;
}</span>

4.  http://acm.hdu.edu.cn/showproblem.php?pid=1709
給你天平和砝碼,讓你判斷1到s之間的重量那個是不可以求得,s爲所有砝碼的質量之和,這題要注意砝碼既可以放一邊,也可以兩邊都放,那麼就是既有加的情況,也有減的情況。
AC代碼
<span style="font-size:18px;">#include<iostream>
using namespace std;
int abs(int n)
{
    return n<0?(-n):n;
}
int main()
{
    int c1[15000];
    int c2[15000];
    int a[101];
    int result[10000];
    int n;
    while(cin>>n)
    {
        int i,j,k;
        int sum=0;
      memset(c1,0,sizeof(c1));
      memset(c2,0,sizeof(c2));
      for( i=0;i<n;i++)
      {
          cin>>a[i];
          sum+=a[i];
      }
      for( i=0;i<=a[0];i+=a[0])
      {
          c1[i]=1;
      }
      for(i=1;i<n;i++)
      {
          for(j=0;j<=sum;j++)
          {
              for(k=0;k<=a[i];k+=a[i])
              {
                  c2[k+j]+=c1[j];
                  c2[abs(k-j)]+=c1[j];
              }
          }
          for(j=0;j<=sum;j++)
            {
              c1[j]=c2[j];
              c2[j]=0;
          }
      }
      int count=0;
      for(i=0;i<=sum;i++)
      {
          if(c1[i]==0)
          {
         result[count++]=i;
          }
      }
      cout<<count<<endl;
      if(count!=0)
      {
          for(i=0;i<count-1;i++)
          {
              cout<<result[i]<<' ';
          }
          cout<<result[count-1]<<endl;
      }
    }
    return 0;
}

</span>

5.  http://acm.hdu.edu.cn/showproblem.php?pid=1171
這題同樣用母函數來解,只不過注意指數的變化範圍,還有要求的是sum/2左右係數不爲0的指數
AC代碼
<span style="font-size:18px;">#include<iostream>
using namespace std;
struct f
{
    int v;
    int num;
}st[70];
int c1[260000];
int c2[260000];
int main()
{
    int n,len;
    while (cin >> n&&n > 0)
    {
        len = 0;
        for (int i = 0; i < n; i++)
        {
            cin >> st[i].v >> st[i].num;
            len += st[i].v*st[i].num;
        }
        
        memset(c1, 0, len*sizeof(c1[0]));
        memset(c2, 0, len*sizeof(c2[0]));
        for (int i = 0; i <= st[0].num*st[0].v; i+=st[0].v)
        {
            c1[i] = 1;
        }
        for (int i = 1; i <= n-1; i++)
        {
            for (int j = 0; j <= len; j++)
            {
                for (int k = 0;(k<=st[i].num*st[i].v)&& (k+j <= len); k += st[i].v)
                {
                    c2[j + k] += 1 * c1[j];
                }
            }
            for (int i = 0; i <= len; i++)
            {
                c1[i] = c2[i];
                c2[i] = 0;
            }
        }
        int v;
        for ( v = (len >> 1); v >= 0; v--)
        {
            if (c1[v]!=0)
            {
                break;
            }
        }
        cout << len - v<< ' ' << v << endl;
    }
    return 0;
}</span>

6.  http://acm.hdu.edu.cn/showproblem.php?pid=2069
給你1,5,10,25,50不同的硬幣,在給你一個錢數,問這些硬幣組成這個錢數有多少種不同的方法,但是要求所需的總的硬幣個數要小於等於100
AC代碼
<span style="font-size:18px;">#include<iostream>
using namespace std;
int main()
{
    int n;
    int c1[500][102];//前面表示硬幣能夠表示的錢數,後面表示表示這個錢數需要多少硬幣
    int c2[500][102];//前面表示硬幣能夠表示的錢數,後面表示表示這個錢數需要多少硬幣
    int a[5]={1,5,10,25,50};
    while(cin>>n)
    {
        memset(c1,0,sizeof(c1));
        memset(c2,0,sizeof(c2));
        int i,j,k;
     for(i=0;i<=100;i++)
     {
         c1[i][i]=1;
     }     
     for( i=1;i<5;i++)
     {
         for(j=0;j<=n;j++)
         {
             for(k=0;k+j<=n;k+=a[i])
             {
                for(int v=0;v+k/a[i]<=100;v++)//判斷所需硬幣數量有沒有超過100
                 c2[k+j][v+k/a[i]]+=c1[j][v];//表示同一錢幣所以不同的硬幣數量相加
             }    
         }
         for(k=0;k<=n;k++)
         {
         for(j=0;j<102;j++)
         {
             c1[k][j]=c2[k][j];
             c2[k][j]=0;
         }
         }
     }
     int sum=0;
     for(i=0;i<=100;i++)
     {
         sum+=c1[n][i];//得到錢數小於100總的表示方法
     }
     cout<<sum<<endl;
    }
    return 0;
}</span>

7 . http://acm.hdu.edu.cn/showproblem.php?pid=2152
AC代碼 同樣簡單母函數
<span style="font-size:18px;">#include<iostream>
using namespace std;
int main()
{
    int n,m;
    int a[101][2];
    int c1[200];
    int c2[200];
    while(cin>>n>>m)
    {
        int i,j,k;    
        for(i=0;i<n;i++)
        {
            cin>>a[i][0]>>a[i][1];
        }
        memset(c1,0,sizeof(c1));
        memset(c2,0,sizeof(c2));
            for(i=a[0][0];i<=a[0][1];i++)
            {
                c1[i]=1;
            }
            for(i=1;i<n;i++)
            {
                for(j=0;j<=m;j++)
                {
                    for(k=a[i][0];k<=a[i][1];k++)
                    {
                        c2[j+k]+=c1[j];
                    }
                }
                for(j=0;j<=m;j++)
                {
                    c1[j]=c2[j];
                    c2[j]=0;
                }
            }
            cout<<c1[m]<<endl;
    }
    return 0;
}</span>

8.http://acm.hdu.edu.cn/showproblem.php?pid=2566
和上面一題2069一樣
AC代碼
<span style="font-size:18px;">#include<iostream>
using namespace std;
int c1[1000][1000];
int c2[1000][1000];
int main()
{
    int n,m,t;

    int a[3]={1,2,5};
    int i,j,k,v;
    cin>>t;
    while(t--)
    {
        cin>>n>>m;
        memset(c1,0,sizeof(c1));
        memset(c2,0,sizeof(c2));
        for( i=0;i<=n;i++)
        {
            c1[i][i]=1;
        }
        for(i=1;i<3;i++)
        {
            for(j=0;j<=m;j++)
            {
                for(k=0;k+j<=m;k+=a[i])
                {
                    for(v=0;v+k/a[i]<=n;v++)
                    {
                        c2[k+j][v+k/a[i]]+=c1[j][v];
                    }
                }
            }
            for(j=0;j<=m;j++)
            {
                for(k=0;k<=n;k++)
                {
                    c1[j][k]=c2[j][k];
                    c2[j][k]=0;
                }
            }
        }
        cout<<c1[m][n]<<endl;
    }
    return 0;
}</span>


9.http://acm.hdu.edu.cn/showproblem.php?pid=2189
注意組成數爲素數,所以次方相加爲素數的倍數
AC代碼
<span style="font-size:18px;">#include<iostream>
using namespace std;
int prime[100];
bool isprime(int n)
{
    for(int i=2;i*i<=n;i++)
    {
        if(n%i==0)
            return false;
    }
    return true;
}

int main()
{
    int key=0;
    int i,j,k,t;
for(i=2;i<=150;i++)
{
    if(isprime(i))
    {
        prime[key++]=i;
    }
}
int n;
cin>>t;
int c1[200];
int c2[200];
while(t--)
{
    cin>>n;
memset(c1,0,sizeof(c1));
memset(c2,0,sizeof(c2));
for(i=0;i<=n;i+=prime[0])
{
    c1[i]=1;
}
for(i=1;i<key;i++)
{
    for(j=0;j<=n;j++)
    {
        for(k=0;k+j<=n;k+=prime[i])
        {
            c2[j+k]+=1*c1[j]+0*c2[k];
        }
    }
    for(j=0;j<=n;j++)
    {
        c1[j]=c2[j];
        c2[j]=0;
    }
}
cout<<c1[n]<<endl;
}
return 0;
}</span>


太晚了,先寫這麼多吧




發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章