概要
遺跡発掘? 何種類かのn個の建物があり、K個選ぶ。選んだ建物の種類一覧が与えられる。選び方は何通り?
サンプル
建物:{1, 2, 2, 1}
種類:{1, 2}
数:2
1, 2, 2, 1の中から2個選び出したら、1と2が入っていた(つまり1と2だった)選び方は?
もちろん(1の選び方)*(2の選び方)=2*2=4
建物:{1, 2, 2, 1}
種類:{1}
数:2
2個選んだら、1のみが入っていた
つまり両方1だったという事なので1通り
制約
建物の種類は1~50
1≦n≦50
1≦k≦n
解法
DP
d[i][j]:i番目までの種類でk個選ぶ選び方
d[i][j] = Σ(dp[i-1][j-k]*b[i]Ck):(k=1~min(b[k], j))
ただしb[i]は種類がiの建物の数
ソースコード
#include <cstdio> #include <cmath> #include <cstring> #include <ctime> #include <iostream> #include <algorithm> #include <set> #include <vector> #include <sstream> #include <typeinfo> using namespace std; typedef long long ll; class Excavations2 { ll dp[52][52]; ll comb[52][52]; public: long long count(vector<int> kind, vector<int> found, int K) { for (int i = 0; i < 52; i++) { for (int j = 0; j < 52; j++) { if (j > i) comb[i][j] = 0; else { ll result = 1; for (int l = 1; l <= j; l++) { result *= i - l + 1; result /= l; } comb[i][j] = result; } } } std::vector<int> b; for (int i = 0; i < found.size(); i++) { b.push_back(std::count(kind.begin(), kind.end(), found[i])); } for (int i = 1; i <= K; i++) { if (i > b[0]) dp[0][i] = 0; else dp[0][i] = comb[b[0]][i]; } for (int i = 1; i < b.size(); i++) { for (int j = 1; j <= K; j++) { dp[i][j] = 0; for (int l = 1; l <= b[i]; l++) { if (j < l) break; dp[i][j] += dp[i-1][j-l]*comb[b[i]][l]; } } } return dp[b.size()-1][K]; } };