#include
#include
#include
using namespace std;
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
int a, b;
cin >> a >> b;
int res = gcd(a, b);
if (res == 1) cout << "YES" << "\n";
else cout << "NO" << "\n";
return 0;
}
朴素做法时间复杂度 O ( n 2 log n ) O(n ^ 2\log{}{n}) O(n2logn), 会超时
#include
#include
#include
using namespace std;
const int N = 1e5 + 10;
int n;
int w[N], f[N];
int gcd(int a, int b) {
return b ? gcd(b, a % b) : a;
}
bool check(int a, int b) {
return gcd(a, b) == 1;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i) cin >> w[i];
int res = 1;
for (int i = 1; i <= n; ++i) {
f[i] = 1;
for (int j = 1; j < i; ++j) {
if (!check(w[j], w[i])) f[i] = max(f[i], f[j] + 1);
}
res = max(res, f[i]);
}
cout << res << "\n";
return 0;
}
需要优化, 如何找到 a i a_i ai前面与 a i a_i ai不互质的数的位置, a i a_i ai质因数的个数是 log a i \log{}{a_i} logai, 与 a i a_i ai不互质的数的位置一定至少与 a i a_i ai有一个公共的质因子
原来是暴力枚举所有非互质数, 现在可将整个集合进行划分, 划分成与 a i a_i ai有相同的质因子是 p 1 p_1 p1的数, 是 p 2 p_2 p2的数…
g [ i ] g[i] g[i]代表所有包含质因子 i i i的 f [ j ] f[j] f[j]的最大值, 这样处理后状态转移方程转化为
f [ i ] = max ( g [ 2 ] , g [ 3 ] , . . . , g [ k ] ) + 1 f[i] = \max(g[2], g[3], ..., g[k]) + 1 f[i]=max(g[2],g[3],...,g[k])+1, 求 f [ i ] f[i] f[i]后更新 g g g
时间的复杂度 O ( n n ) O(n\sqrt n) O(nn), 时间瓶颈在试除法分解质因数, 或者使用筛法优化, 时间复杂度 O ( n log n ) O(n\log n) O(nlogn)
#include
#include
#include
using namespace std;
const int N = 1e5 + 10;
int n;
int w[N], f[N], primes[N], g[N];
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
for (int i = 1; i <= n; ++i) cin >> w[i];
// 筛法求每个数的最小质因子
for (int i = 2; i < N; ++i) {
for (int j = i; j < N; j += i) {
if (!primes[j]) primes[j] = i;
}
}
int res = 0;
for (int i = 1; i <= n; ++i) {
f[i] = 1;
// 枚举每个数的质因子
for (int j = w[i]; j > 1; j /= primes[j]) {
f[i] = max(f[i], g[primes[j]] + 1);
}
// 更新每个质因子对应的最大的f的值
for (int j = w[i]; j > 1; j /= primes[j]) {
g[primes[j]] = max(g[primes[j]], f[i]);
}
res = max(res, f[i]);
}
cout << res << "\n";
return 0;
}
注意 j ≠ 0 j \ne 0 j=0, 因为 0 0 0没有最小质因子
将问题集合进行分类, 将集合按照第 1 1 1个不能上车的奶牛进行划分, 如果所有奶牛都能上车进行特判
将集合继续进行划分, 前面有 j j j头牛, 后面有 n − j − 1 n - j - 1 n−j−1头牛, 在对重量进行划分
f [ i ] [ j ] f[i][j] f[i][j]表示选择了 j j j头牛并且总重量是 k k k的方案数, 发生的概率就是 f [ i ] [ j ] n ! \frac{f[i][j]}{n!} n!f[i][j], 因为前后奶牛的顺序可以任意调整, 因此概率为
f [ i ] [ j ] × j ! × ( n − j − 1 ) ! n ! \frac{f[i][j] \times j! \times (n - j - 1)!}{n!} n!f[i][j]×j!×(n−j−1)!
如何计算 f [ j ] [ k ] f[j][k] f[j][k]?
从 n n n头牛中选择 j j j头牛并且总量为 k k k的方案数, 是二维费用的背包问题
因此总的期望就是
∑ i = 1 n f [ j , k ] × j ! × ( n − i − 1 ) ! n ! j \sum_{i = 1}^{n} \frac{f[j, k] \times j! \times (n - i - 1)!}{n!} j i=1∑nn!f[j,k]×j!×(n−i−1)!j
将式子进行变形
∑ i = 1 n j × f [ j ] [ k ] n j ! × ( n − j − 1 ) ! ( n − 1 ) ! \sum_{i = 1}^{n} \frac {j \times f[j][k]}{n} \frac {j! \times (n - j - 1)!}{(n - 1)!} i=1∑nnj×f[j][k](n−1)!j!×(n−j−1)!
发现后面的式子是组合数倒数
∑ i = 1 n j × f [ j ] [ k ] n 1 C n − 1 j \sum_{i = 1}^{n} \frac {j \times f[j][k]}{n}\frac {1}{C_{n - 1} ^ {j}} i=1∑nnj×f[j][k]Cn−1j1
时间复杂度 O ( n 4 ) O(n ^ 4) O(n4)
在枚举 k k k时需要考虑加入当前 a c a_c ac后超重因此 a c + k > m a_c + k > m ac+k>m并且 k ≤ m k \le m k≤m
#include
#include
#include
using namespace std;
typedef long long LL;
const int N = 55, M = N * N;
int n, m;
int w[N];
// f[i][j]表示从n头牛中选择i头牛并且总重量为j的所有方案
LL f[N][M];
// 计算组合数
LL C(int a, int b) {
if (a < b) return 0;
LL res = 1;
for (int i = a, j = 1; j <= b; ++j, --i) {
res = res * i / j;
}
return res;
}
int main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
cin >> n;
int sum = 0;
for (int i = 1; i <= n; ++i){
cin >> w[i];
sum += w[i];
}
cin >> m;
// 汽车能装在全部的奶牛
if (sum <= m) {
cout << n << "\n";
return 0;
}
double res = 0;
// 枚举哪一头牛是超重的牛
for (int c = 1; c <= n; ++c) {
memset(f, 0, sizeof f);
f[0][0] = 1;
for (int i = 1; i <= n; ++i) {
// 因为第c头奶牛超重
if (i == c) continue;
for (int j = i; j; --j) {
for (int k = m; k >= w[i]; --k) {
f[j][k] += f[j - 1][k - w[i]];
}
}
}
// 因为已经选择了一个奶牛 因此剩余奶牛数量是n - 1
for (int j = 0; j < n; ++j) {
for (int k = max(m - w[c] + 1, 0); k <= m; ++k) {
double val = (double) f[j][k] / C(n - 1, j) / n * j;
res += val;
}
}
}
cout << res << "\n";
return 0;
}