1. 程式人生 > >sincerit 母函式(組合問題)

sincerit 母函式(組合問題)

大佬程式碼: https://blog.csdn.net/yu121380/article/details/79914529
https://blog.csdn.net/xiaofei_it/article/details/17042651?utm_source=blogxgwz0
有1克、2克、3克、4克的砝碼各一 枚,能稱出哪幾種重量?各有幾種可能方案?
分析: 我們假設x表示砝碼, x的指數表示砝碼的重量,這樣:
1個1克的砝碼可以用函式1+x表示
1個2克的砝碼可以用函式1+x2 表示
1個3克的砝碼可以用函式1+x3 表示
1個4克的砝碼可以用函式1+x4 表示
因為每種砝碼只有一個,所以1是表示對該種砝碼不取。
幾種砝碼組合可以稱重的情況, 可以用以上幾個函式的乘積表示:
(1+x)(1+x2

)(1+x3 )(1+x4 )
所有組合的情況就是展開後的式子:
1 + x + x2 + 2x3 +2x 4 + 2x 5 + 2x 6 + 2x 7 + x 8 + x 9 + x 10
從上面的函式(母函式)知道可稱出從1克到10克的重量,係數便是各種重量的方案數

有2個骰子投擲出6點, 共有多少種情況
分析:
我們可以設想骰子出現的點數1, 2, 3, 4, 5, 6和 t , t2 , t3 ,t 4 ,t 5 ,t 6對應起來,則第一個骰子可能出現的點數就與(t+t2 +t3 +t 4 +t 5 +t 6)中的t的各次冪一一對應。
若兩個骰子(t+t2 +t3

+t 4 +t 5 +t 6) * (t+t2 +t3 +t 4 +t 5 +t 6) = t2+2t3+3t4+4t5+…中的t6的係數為5, 顯然 組成6點的可能有1+5,2+4,3+3, 4+2,5+1這5種方案數

Ignatius and the Princess III
杭電的整數劃分母函式實現
輸入的n相當於n種砝碼(1~n)且每一種有無數多個

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
int sup[150]; int temp[150]; int main() { int n; while (cin >> n) { // 初始化第一個大括號 for (int i = 0 ; i <= n; i++) { sup[i] = 1; temp[i] = 0; } // 還剩下n-1個大括號,,且從第2個開始 for (int i = 2; i <= n; i++) { // 遍歷當前結果多項式的每一項 for (int j = 0; j <= n; j++) { for (int k = 0; k + j <= n; k+=i) { // 遍歷當前大括號的每一項 temp[j+k] += sup[j]; } } for (int j = 0 ; j <= n; j++) { sup[j] = temp[j]; temp[j] = 0; } } printf("%d\n", sup[n]); } return 0; }
#include <iostream>    
using namespace std;        
const int mx = 1000;     
// sup是儲存多項式的陣列,sup[n]中的值代表xn的係數  
// temp是臨時多項式,儲存相乘的臨時中間情況    
int sup[mx], temp[mx];     
/* 
程式始終只計算兩個多項式之間的乘積,多個多項式的情況 
先計算前兩個的乘積,將結果作為第一個多項式,再與第三個相乘 
依次類推,sup始終存放當前運算後的結果然後作為被乘多項式, 
*/    
int main()    
{     
    int target;   //  目標重量, 比如上面的例子裡就是10,要<max的值  
    int i, j, k;    
     
    while(cin >> target)    
    {    
        for(i=0; i<=target; ++i)       
        {    
            sup[i] = 1;     
//初始化第一個多項式,也就是用1g砝碼的多項式,  
//注意如果題目沒給1g的砝碼那麼就不能++i,而要加上砝碼的質量  
            temp[i] = 0;    
//將臨時區清空,無論第一個多項式質量是幾都要全部置零  
        }    
        for(i=2; i<=target; ++i)     
// 生成後續的第i個多項式,此題中是2g,i從2開始。  
//如果砝碼的值不是規律增長,i可能需要取決於輸入  
        {    
     
            for(j=0; j<=target; ++j)     
// 遍歷當前結果多項式的每一項(當前結果的第j項)與第i個多項式相乘,  
                for(k=0; k+j<=target; k+=i)   // 每一個係數都為1就加sup[j] 
// 遍歷第i個多項式的每一項,此處構造用小砝碼組成大砝碼的多項式  
                {    
                    temp[j+k] += sup[j];    
//冪運算,注意理解  
                }    
            for(j=0; j<=target; ++j)      
// 將臨時的結果覆蓋當前結果,同時把臨時結果置零,為下次做準備  
            {    
                sup[j] = temp[j];    
                temp[j] = 0;    
            }    
        }    
        cout << sup[target] << endl;  //輸出結果  
    }    
    return 0;    
}

Big Event in HDU
一般動態規劃過不了

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
#define CLR(array, value) memset(array, value, sizeof(value));
#define N 2500005
int dp[N];
// dp[j]揹包容量為j時的最大價值
// 轉移方程
// 初始化條件 dp[0] = 0
int num[N];
int main() {
  int n, v, m;
  while (cin >> n) {
    if (n <= 0) break;
    int a, b, k = 0, sum = 0;
    while (n--) {
      cin >> a >> b;
      while (b--) {
        num[k++] = a;
        sum += a;
      }
    }
    dp[0] = 0;
    int V = sum / 2;
    for (int i = 0; i < k; i++) {
      for (int j = V; j >= num[i]; j--) {
        dp[j] = max(dp[j], dp[j-num[i]]+num[i]);
      }
    }
    sum - dp[V] > dp[V] ? printf("%d %d\n", sum-dp[V], dp[V]) : printf("%d %d\n", dp[V], sum-dp[V]);
  }
  return 0;
}

母函式: https://blog.csdn.net/jk13171217/article/details/38303111
題意相當於有n種砝碼, 每種砝碼重量是weight[i], 並且每一種砝碼的個數是number[i],求把這n個砝碼平均分兩堆,第一堆重量大於等於第二堆

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
#define MAX 250005
#define N 200
int number[N], weight[N], sup[MAX], temp[MAX];
int main() {
  int n, v, m;
  while (cin >> n && (n >= 0)) {
    int k = 0, sum = 0;
    while (n--) {
      cin >> weight[k] >> number[k];
      sum += weight[k] * number[k];
      k++;
    }
    int V = sum / 2;
    memset(sup, 0, sizeof(sup));
    memset(temp, 0, sizeof(temp));
    // 第一個大括號(1 + x^weight + x^2weight + .. +x^number[k]*weight) 
    for (int i = 0; i <= number[0]; i++)
      sup[weight[0] * i] = 1;
    // 從第二個大括號起 (1 + x^weight[1] + x^2weight[1] + ...x^number[1]weight[1]); 
    for (int i = 1; i < k; i++) {
      for (int j = 0; j <= V; j++) {
        // 無論多少個k x^k*weight[i]的係數都為1,就如(1+x^2+x^4+x^6+x^8) 兩克的砝碼有四個一樣 
        for (int k = 0; k*weight[i] <= V && k <= number[i]; k++) // k是第i種砝碼的個數
          temp[k*weight[i]+j] += sup[j];
      }
      for (int j = 0; j <= V; j++) {
        sup[j] = temp[j];
        temp[j] = 0;
      } 
    }
    // 並不是所有的重量都可以湊出,所以就是一半以下得到第一個能組合出的重量的係數說明該重量就是答案 
    int i;
    for (i = V; i >= 0; i--) if (sup[i]) break;
    printf("%d %d\n", sum-i, i); 
  }
  return 0;
}

Square Coins
題意: 相當於有17種砝碼,每一種砝碼的重量為12, 22, 32, … 172, 每一種砝碼有無限多個
問給定一個重量n,問湊成這個重量的方案數是多少?
(1+t+t2 +t3 +…+tn)(1+t+t4 +t 2 * 4 +t 3 * 4 …+tn) (1+t+t9 +t 2 * 9 +t 3 * 9 …+tn)…(1+t+t17 +t 2 * 17 +t 3 * 17 …+tn)
有17個大括號
母函式:

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
int sup[1000], temp[1000];
int coin[20];
int main() {
  int n;
  while (cin >> n, n) {
    for (int i = 1; i <= 17; i++) coin[i] = i*i;
    memset(sup, 0, sizeof(sup));
    memset(temp, 0, sizeof(temp));
    // 第一個大括號 雖然有無限多個但只要初始化到相等的重量就可以 
    for (int i = 0; i <= n; i++) sup[i] = 1;
    // 還有16個大括號 
    for (int i = 2; i <= 17; i++) {
      // 遍歷當前結果的每一項
      for (int j = 0; j <= n; j++) {
        // k表示coin[i]的個數 這個迴圈是遍歷第i個大括號裡的每一項 
        for (int k = 0; k*coin[i]+j <= n; k++) {
          temp[k*coin[i]+j] += sup[j];
        }
      }
      for (int j = 0; j <= n; j++) {
        sup[j] = temp[j];
        temp[j] = 0;
      }
    }
    cout << sup[n] << "\n"; 
  }
  return 0;
}

動態規劃解法–完全揹包
dp[i][j] 表示前i種硬幣能組成紙幣數為j的方案總數
對於第k種硬幣 當手中有j-k個硬幣的方案數為dp[i-1][j-k]種時再加k個硬幣就能組成紙幣j的方案數
轉移方程: dp[i][j] = dp[i][j] + dp[i-1][j-coin[k]];
初始化條件 dp[1~n][0] = dp[0][1~n] = 0;
換成一維陣列 dp[0] = 1; 表示剛好能換成硬幣 dp[4-4] = 1剛好有一個為硬幣為4的換成紙幣種類數為1, 相當於遞迴的出口

#include <stdio.h>
#include <string.h>
int dp[1000];
int coin[20];
int main() {
	int n;
	for (int i = 1; i <= 17; i++) coin[i] = i * i;
	while (scanf("%d", &n), n) {
	  memset(dp, 0, sizeof(dp)); // 除了dp[0]=1外其他要初始化為0 
	  dp[0] = 1; // 表示剛好能換成硬幣 
	  for (int i = 1; i <= 17; i++) {
	    for (int j = coin[i]; j <= n; j++) {
	      dp[j] = dp[j] + dp[j-coin[i]];
      }
    }
    printf("%d\n", dp[n]); 
  }
  return 0;
}

2082 找單詞
輸入的26個數字是各個砝碼的數量,而各個砝碼的重量就是對應的1~26
求小於等於50以下的所有重量的組合總數

#include <iostream>
#include <cstring>
using namespace std;
typedef long long ll;
ll sup[100], temp[100];
ll p[30];
int main() {
  int n;
  cin >> n;
  while (n--) {
    for (int i = 1; i <= 26; i++) cin >> p[i];  // 代表個數 
    memset(temp, 0, sizeof(temp));
    memset(sup, 0, sizeof(sup));
    sup[0] = 1;
    for (int i = 1; i <= 26; i++) { // 同時i代表每種砝碼重量 
      for (int j = 0; j <= 50; j++) {
        for (int k = 0; k <= p[i] && k * i + j <= 50; k++) {  // 發現k<=p[i]不能少 
          temp[k*i+j] += sup[j];
        }
      }
      for (int j = 0; j <= 50; j++) {
        sup[j] = temp[j];
        temp[j] = 0;
      }
    }
    ll sum = 0;
    for (int j = 50; j >= 1; j--) sum += sup[j];  // 因為0代表重量為0沒有選砝碼,所以不加上0的sup
    cout << sum << "\n";
  }
  return 0;
}

其他方法:用混合揹包

2110 Crisis of HDU

#include <iostream>
#include <stdio.h>
#include <cstring>
#include <algorithm>
using namespace std;
#define N 10000
#define MOD 10000
int sup[N+5], temp[N+5];
int weight[105], number[105];
int main() {
  int n;
  while (cin >> n, n) {
    int sum = 0;
    for (int i = 1; i <= n; i++) {
      cin >> weight[i] >> number[i];
      sum = sum + weight[i] * number[i];  // 這裡蠢了一下 
    }
    if (sum % 3 != 0) {
      cout << "sorry\n";
      continue;
    }
    memset(sup, 0, sizeof(sup));
    memset(temp, 0, sizeof(temp));
    for (int i = 0; i <= number[1]; i++) sup[i*weight[1]] = 1;
    int V = sum / 3;
    for (int i = 2; i <= n; i++) {
      for (int j = 0; j <= V; j++) {
        for (int k = 0; k <= number[i] && k*weight[i]+j <= V; k++) {
          temp[k*weight[i]+j] =  (temp[k*weight[i]+j] + sup[j]) % MOD;
        }
      }
      for (