1 条题解
-
0
题解
核心思路是把“左右抽屉中存在某个颜色的手套同时被取走”转化为 Pigeonhole 思维。若某个颜色只在一侧出现(另一侧数量为 0),那一侧的所有手套都必须拿走,否则永远不可能凑成这一颜色的配对——代码用
rebu_a/rebu_b
事先统计这些必取数量,并把对应颜色剔除。对剩余颜色,假设我们决定取走其中一部分颜色的所有左手手套,并把其它颜色交给右手抽屉负责。若我们从左边取走集合 的全部手套,那么只要右边保证至少有一个颜色来自 ,就能配对;若 并非全部颜色,还需要在左边额外多拿 1 只手套以跨过抽屉边界的“安全线”。右边也有完全对称的考虑:若右侧负责集合 的全部颜色,当 不是全集时,也要再加 1。
因此可以枚举剩余颜色的所有划分。程序把每个子集对应的“左侧总数
lef
”与“其余颜色右侧总数rig
”记录下来,按lef
递减排序,并用扫描的方式维护当前遇到的最大rig
。对每个候选子集 (记录为lef
)与之前扫描到的最佳对向集合(记录为ma
),尝试更新答案:左边需取lef + (lef != Sum_a)
,右边需取ma + (ma != Sum_b)
。二者之和即当下策略的总手套数。历遍所有子集即可得到最小的左右抽屉取值,再把之前剔除的必取手套加回去就是最终答案。由于有效颜色最多 20 种,暴力子集的复杂度为 ,完全可行。
/* Trần Duy Vũ - THPT Chuyên Lương Thế Vinh */ #include<bits/stdc++.h> using namespace std; using namespace chrono; using ll = long long; template <class X, class Y> void ckmin(X &x, const Y &y) { if (x > y) x = y;} template <class X, class Y> void ckmax(X &x, const Y &y) { if (x < y) x = y;} #define Duy_vu_dep_trai int main Duy_vu_dep_trai() { ios_base::sync_with_stdio(false); cin.tie(nullptr); int n; cin >> n; vector<int> a(n), b(n); for (int i= 0; i< n; i++) { cin >> a[i]; } for (int i= 0; i< n; i++) { cin >> b[i]; } int Sum_a= 0, Sum_b= 0, rebu_a= 0, rebu_b= 0; for (int i= 0; i< n; i++) { if(!a[i] || !b[i]) { rebu_a+= a[i]; rebu_b+= b[i]; swap(a[i], a[n - 1]); swap(b[i], b[n - 1]); n --; i--; } else { Sum_a+= a[i]; Sum_b+= b[i]; } } a.resize(n); b.resize(n); vector<pair<ll, ll>> pairs; for (int m1= 0; m1 < (1<< n); m1++) { int lef= 0, rig= 0; for(int i= 0; i< n; i++) { if(m1>>i & 1) { lef+= a[i]; } else { rig+= b[i]; } } pairs.push_back(make_pair(lef, rig)); } sort(pairs.begin(), pairs.end()); reverse(pairs.begin(), pairs.end()); int ma= 0, Answers_a=1e9, Answers_b=1e9; for (int i= 0; i< pairs.size(); i++) { int x=pairs[i].first, y=pairs[i].second; if(x+ (x!= Sum_a) + ma + (ma!= Sum_b) < Answers_a+ Answers_b) { Answers_a= x+ (x!= Sum_a); Answers_b= ma + (ma!= Sum_b); } ckmax(ma, y); } cout<<Answers_a+rebu_a <<"\n" <<Answers_b+rebu_b <<endl; }
- 1
信息
- ID
- 3241
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 6
- 标签
- 递交数
- 1
- 已通过
- 1
- 上传者