Top

(ΦωΦ)<これの答えですよー。目次はここ

---
問4-5
(ΦωΦ)<再帰 その1
#include <iostream>
#include <algorithm>
#include <cassert>
using namespace std;
using i64 = long long;

class Excavations2 { public: i64 count(vector<int> kind, vector<int> found, int K); };

vector<vector<i64>> nCr;

void init_comb(int n) {
  nCr.assign(n, vector<i64>(n));
  for(int i=0; i<n; i++) { nCr[i][0] = 1; }
  for(int i=1; i<n; i++) for(int j=1; j<n; j++) nCr[i][j] = nCr[i-1][j] + nCr[i-1][j-1];
}

vector<int> cnt, // 昔i番目の種類の建物はcnt[i]個あった。
            found; // 考古学者はfound[i]という種類の建物を発見した。
vector<vector<i64>> dp;

// 考古学者が発見したi番目までのi+1種類でj個の建物が取りうるパターン数を返す。
i64 rec(int i, int j) {
  if(i < 0) { return j==0; } // 考古学者は建物を1種類も発見してない。
  i64 &res = dp[i][j];
  if(res != -1) { return res; }
  int x = cnt[found[i]]; // 考古学者が発見したi番目の種類の建物は昔x個あった。
//  assert(x > 0); // x > 0は保証されている
  res = 0;
  for(int k=1; k<=min(x, j); k++) {
    // 昔x個あったi番目の建物のうちk個発見したときは、
    // 残りのi-1番目までのi種類の建物は全部でj-k個発見した。
    // kは1以上x以下、あるいは1以上j以下。
    // x個からk個を選ぶパターン数はnCr(x, k)。
    res += rec(i-1, j-k) * nCr[x][k]; 
  }
  return res;
}

i64 Excavations2::
count(vector<int> kind, vector<int> _found, int K) {
  found = _found;
  int m = kind.size(), // 昔あった建物の種類数
      n = found.size(); // 考古学者が発見した建物の種類数
  init_comb(51);
  cnt.resize(51);
  for(int i=0; i<m; i++) { cnt[kind[i]]++; }
  dp.assign(n, vector<i64>(51, -1));
  i64 res = rec(n-1, K);
  return res;
}
    

(ΦωΦ)<再帰 その2
#include <iostream>
#include <algorithm>
#include <cassert>
using namespace std;
using i64 = long long;

class Excavations2 { public: i64 count(vector<int> kind, vector<int> found, int K); };

vector<vector<i64>> nCr;

void init_comb(int n) {
  nCr.assign(n, vector<i64>(n));
  for(int i=0; i<n; i++) { nCr[i][0] = 1; }
  for(int i=1; i<n; i++) for(int j=1; j<n; j++) nCr[i][j] = nCr[i-1][j] + nCr[i-1][j-1];
}

vector<int> cnt, // 昔i番目の種類の建物はcnt[i]個あった。
            found; // 考古学者はfound[i]という種類の建物を発見した。
vector<vector<i64>> dp;

// 考古学者が発見したi種類j個の建物が取りうるパターン数を返す。
i64 rec(int i, int j) {
  i64 &res = dp[i][j];
  if(res != -1) { return res; }
  if(i == 0) { return res = j==0; } // 考古学者は建物を1種類も発見してない。
  int x = cnt[found[i-1]]; // 考古学者が発見したi種類の建物の最後(=i-1番目)は昔x個あった。
//  assert(x > 0); // x > 0は保証されている
  res = 0;
  for(int k=1; k<=min(x, j); k++) {
    // 昔x個あったi-1番目の建物のうちk個発見したときは、
    // 残りのi-1種類の建物は全部でj-k個発見した。
    // kは1以上x以下、あるいは1以上j以下。
    // x個からk個を選ぶパターン数はnCr(x, k)。
    res += rec(i-1, j-k) * nCr[x][k]; 
  }
  return res;
}

i64 Excavations2::
count(vector<int> kind, vector<int> _found, int K) {
  found = _found;
  int m = kind.size(), // 昔あった建物の種類数
      n = found.size(); // 考古学者が発見した建物の種類数
  init_comb(51);
  cnt.resize(51);
  for(int i=0; i<m; i++) { cnt[kind[i]]++; }
  dp.assign(n+1, vector<i64>(51, -1));
  i64 res = rec(n, K);
  return res;
}
    

(ΦωΦ)<forループ
#include <iostream>
#include <algorithm>
using namespace std;
using i64 = long long;

class Excavations2 { public: i64 count(vector<int> kind, vector<int> found, int K); };

vector<vector<i64>> nCr;

void init_comb(int n) {
  nCr.assign(n, vector<i64>(n));
  for(int i=0; i<n; i++) { nCr[i][0] = 1; }
  for(int i=1; i<n; i++) for(int j=1; j<n; j++) nCr[i][j] = nCr[i-1][j] + nCr[i-1][j-1];
}

vector<vector<i64>> dp;

i64 Excavations2::
count(vector<int> kind, vector<int> found, int K) {
  int m = kind.size(), 
      n = found.size();
  init_comb(51);
  vector<int> cnt(51);
  for(int i=0; i<m; i++) { cnt[kind[i]]++; }
  dp.assign(n+1, vector<i64>(51, 0));
  dp[0][0] = 1;
  for(int i=0; i<n; i++) {
    for(int j=0; j<K; j++) {
      for(int k=1; k<=K-j; k++) {
        int x = cnt[found[i]];
        dp[i+1][j+k] += dp[i][j] * nCr[x][k];
      }
    }
  }
  i64 res = dp[n][K];
  return res;
}
    

(ΦωΦ)<おしまい