学习学习。。。
给定一个数组,要把这个数组中的一部分数给A,另一部分给B,使A和B的和相同但是A中最小的数不小于B中最大的数。例如:
values = {1,2,5,3,4,5}
一共有9中分配方法:
Bob Frank
1,2 3
1,3 4
1,4 5 (first 5)
1,4 5 (second 5)
2,3 5 (first 5)
2,3 5 (second 5)
5 (first 5) 5 (second 5)
5 (second 5) 5 (first 5)
1,2,3,4 5,5
需要注意的是不同位置的相等数字需要单独计算。数组大小最大30,每个数最大1000。
就算知道是动态规划也没做出来。。
先上大神的解法:
#include <algorithm>
#include <functional>
#include <cstdio>
#include <cstdlib>
#include <cctype>
#include <cmath>
#include <iostream>
#include <sstream>
#include <string>
#include <vector>
#include <utility>
using namespace std;
#define REP(i,n) for(int i=0;i<(n);++i)
#define FOR(i,a,b) for(int i=(a);i<=(b);++i)
#define FORD(i,a,b) for(int i=(a);i>=(b);--i)
#define FOREACH(i,c) for(__typeof((c).begin()) i=(c).begin();i!=(c).end();++i)
typedef long long LL;
const int INF = 1000000000;
typedef vector<int> VI;
template<class T> inline int size(const T&c) { return c.size(); }
char buf1[1000];
string i2s(int x) {
sprintf(buf1, "%d", x);
return buf1;
}
const int MAXN = 30;
const int MAX = 30000;
int n;
VI v;
LL B[MAXN + 1][MAX + 1]; // [n pocz][sum]
LL F[MAXN + 1][MAX + 1];
LL nk[MAXN + 1][MAXN + 1];
void cnk() {
nk[0][0] = 1;
FOR(k, 1, MAXN) nk[0][k] = 0;
FOR(n, 1, MAXN) {
nk[n][0] = 1;
FOR(k, 1, MAXN) nk[n][k] = nk[n - 1][k - 1] + nk[n - 1][k];
}
}
void calc(LL T[MAXN + 1][MAX + 1]) {
T[0][0] = 1;
FOR(x, 1, MAX) T[0][x] = 0;
FOR(ile, 1, n) {
int a = v[ile - 1];
FOR(x, 0, MAX) {
T[ile][x] = T[ile - 1][x];
if (x >= a) T[ile][x] += T[ile - 1][x - a];
}
}
}
// MAIN
long long howMany(vector <int> vv) {
v = vv;
n = size(v);
cnk();
sort(v.begin(), v.end(), greater<int>());
calc(F);
sort(v.begin(), v.end());
calc(B);
LL res = 0;
int done = 0;
while (done < n) {
int p = done;
while (p < n && v[p] == v[done]) ++p;
int c = p - done;
FOR(u, 1, c) {
int uu = u * v[done];
FOR(x, uu, MAX)
res += B[done][x - uu] * F[n - done - u][x] * nk[c][u];
}
done = p;
}
return res;
}
按照大神的思路,仿写了一遍:
const int SIZEOFLIST = 30;
const int MAXSUM = 30000;
long long YANGHUI[SIZEOFLIST + 1][SIZEOFLIST + 1];//计算相同数字的取代因子
long long FORWARD[SIZEOFLIST + 1][MAXSUM + 1];//从小到大排列,到第i个数为止,部分和为j的子数组个数
long long BACKWARD[SIZEOFLIST + 1][MAXSUM + 1];//从大到小排列,到第i个数为止,部分和为j的子数组个数
vector<int> vals;
void CalYanghui() {//计算C(i,j),从i个相同的球中取j个的取法
memset(YANGHUI, 0, sizeof(long long) * (SIZEOFLIST + 1) * (SIZEOFLIST + 1));
YANGHUI[0][0] = 1;
for (int i = 1; i <= SIZEOFLIST; i++) {
YANGHUI[i][0] = 1;
for (int j = 1; j <= i; j++) {
YANGHUI[i][j] = YANGHUI[i - 1][j - 1] + YANGHUI[i - 1][j];
}
}
}
void CalSumCount(long long dep[SIZEOFLIST + 1][MAXSUM + 1]){//根据vals值,计算到第i个数为止,部分和为j的子数组个数
memset(dep, 0, sizeof(long long) * (SIZEOFLIST + 1) * (MAXSUM + 1));
dep[0][0] = 1;
int n = vals.size();
for (int i = 1; i <= n; i++) {
int tmpval = vals[i - 1];
for (int j = 0; j <= MAXSUM; j++) {
dep[i][j] = dep[i - 1][j];//至少有[i-1][j]个
if (j >= tmpval) dep[i][j] += dep[i - 1][j - tmpval];//如果把第i个数算入和中,则加上[i-1][j-vals[i]]个
}
}
}
long long howMany1(vector<int> values) {
long long res = 0;
int n = values.size();
vals = values;
CalYanghui();
sort(vals.begin(), vals.end(), greater<int>());
CalSumCount(BACKWARD);
sort(vals.begin(), vals.end());
CalSumCount(FORWARD);
for (int i = 0; i < n;) {
int p = i;
while (p < n && vals[p] == vals[i]) p++;
int c = p - i;//当前数的个数
for (int j = 1; j <= c; j++) {//依次计算有一个、两个..当前数在和中的情况
int tmpsum = j * vals[i];
//当前和到最大和之间
for (int x = tmpsum; x <= MAXSUM; x++) {
//计算从i+j到n的和为x的子数组个数,乘以从0到i+j的和为x的子数组个数,再乘以c个相同数任意挑j个的因子
res += BACKWARD[n - i - j][x] * FORWARD[i][x - tmpsum] * YANGHUI[c][j];
}
}
i = p;
}
return res;
}
继续学习。。