T1:舞会配对

本题难度中等,注意到数据范围很小,正解极有可能是朴素的搜索枚举方法。

m=2n

使用回溯法,依次考虑第 1m 个人,要与谁配对
记录状态:

  • 当前考虑配对的人的编号
  • 已经配成的对数
  • 当前(未完全的)配对方案的幸福度
代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
int main() {
int n;
cin >> n;
int m = 2*n;
vector<vector<int>> a(m, vector<int>(m));
rep(i, m)rep(j, m) cin >> a[i][j];
ll ans = -1e18;
vector<int> matched(m);
auto dfs = [&](auto& f, int step, int cnt, ll now) -> void {
if (cnt == n) {
ans = max(ans, now);
return;
}
if (matched[step]) {
f(f, step+1, cnt, now);
return;
}
for (int i = step+1; i < m; ++i) {
// 1. 剪枝
if (matched[i]) continue;
// 2. 试探
matched[i] = matched[step] = true;
f(f, step+1, cnt+1, now*a[step][i]);
// 3. 回溯
matched[i] = matched[step] = false;
}
};
dfs(dfs, 0, 0, 1);
cout << ans << '\n';
return 0;
}

如何分析时间复杂度?

m=16 的极端情形:

对第 1 个待配对的人,后续有 15 个可选的与其配对的人
对第 2 个待配对的人,后续有 13 个可选的与其配对的人
对第 3 个待配对的人,后续有 11 个可选的与其配对的人

 

对第 8 个待配对的人,后续有 1 个可选的与其配对的人

由乘法原理,需要枚举的所有配对方案为:

15!!=15×13××3×1=2027025

关于本题的背景:

本题为边权之积,可以通过取对数,化积为和。
实质为 二分图最大权匹配问题
时间复杂度为 O(n3)
适用范围:提高组以上

T2:发积分

本题难度较大,显然 x 越大 k 天内能发的分数越多。二分 xcheck(x) 计算发 k 天实际能发多少积分,判断是否能发出 n 分。找到能发完 n 分的最小 x

本题 k 比较小,check 计算时,直接模拟 k 天发积分即可。

使用整除分块技巧,一次可达 Ok,所以 k 的范围可以强化到 1012

显然,x=n 时,可以满足要求:

x1+x2++xkn

答案要求 xmin,而 [xmin,n] 中的任何值都满足要求。

考虑到可能的答案分成了两段,可以考虑使用二分答案法。
二分答案 = 二分框架 + 判定函数。

判断:对于当前的二分值 x,是否满足:

x1+x2++xkn

每次判断时间复杂度 O(k),二分范围为 [1,n]
总时间复杂度 O(klogn)

代码实现
#include <bits/stdc++.h>
#define rep(i, n) for (int i = 0; i < (n); ++i)
using namespace std;
using ll = long long;
int main() {
ll n; int k;
cin >> n >> k;
ll wa = 0, ac = n;
while (ac-wa > 1) {
ll wj = (ac+wa)/2;
auto ok = [&]{
ll res = 0;
for (int i = 1; i <= k; ++i) {
res += (wj+i-1)/i;
}
return res >= n;
}();
(ok ? ac : wa) = wj;
}
cout << ac << '\n';
return 0;
}