// 想定解法: 半分全列挙(蟻本2版p147) #include #include #include #include using namespace std; #define REP(i, n) for(int(i)=0;(i)<(n);++(i)) int main(){ int N, SUM, P[32]; cin >> N >> SUM; if(N < 1 || N > 31 || SUM < 1 || SUM > 310000000){ cerr << "error" << endl; return 1; } REP(i,N){ cin >> P[i]; if(P[i] > 10000000){ cerr << "error" << endl; return 1; } } int L = N/2, R = N - L; vector > list_L, list_R; // 前半の組み合わせを全列挙 list_L.push_back(make_pair(0,0)); REP(i,L){ int size = list_L.size(); REP(j,size){ auto &v = list_L[j]; list_L.push_back(make_pair(v.first + P[i], v.second|(1< > result; // 前半の値1個づつに対して後半のリストから(SUM-値)を2分探索 for(auto &v : list_L){ auto range = equal_range(list_R.begin(), list_R.end(), make_pair(SUM - v.first, 0), [](const pair &p1, const pair &p2){ return(p1.first < p2.first); } ); // 購入パターン構築 for(auto it = range.first; it != range.second; ++it){ int list = it->second | v.second; vector rv; REP(i,N){ if(list & (1<