feat: 添加两道编程题解 P7556 和 P9124

添加两道编程竞赛题解:
1. P7556.cpp 实现集合元素组合验证逻辑
2. P9124.cpp 使用二分查找解决优化问题,处理大数运算防止溢出
This commit is contained in:
Zengtudor 2025-10-20 09:46:06 +08:00
parent 32de376783
commit 88b33ff757
2 changed files with 165 additions and 0 deletions

115
src/10/19/P9124.cpp Normal file
View File

@ -0,0 +1,115 @@
#include <algorithm>
#include <cstdint>
#include <iostream>
#include <istream>
// 使用 long long 防止溢出,特别是 c_i 可以达到 2*10^18
using ll = long long;
#define sl static inline
const ll maxn = 100 + 5;
ll T, n;
// tc, tm 定义为 long long 以匹配 c_i 的数量级,防止计算中溢出
ll tc, tm;
ll a[maxn], b[maxn], c[maxn];
// 检查总花费 S 是否可行
// 替换了原来错误的 checkall 函数
sl bool checkall(ll S) {
// 设 cookie 制作时间减少 x则 muffin 减少 S-x
// 我们需要寻找是否存在一个 x 满足所有约束
// 约束1: x 必须在花费和时间限制内
// x >= 0, S-x >= 0 => 0 <= x <= S
// tc - x >= 1 => x <= tc - 1
// tm - (S-x) >= 1 => x >= S - tm + 1
ll lower_x = std::max(0LL, S - tm + 1);
ll upper_x = std::min(S, tc - 1);
// 如果初始约束已经无解,则该 S 不可行
if (lower_x > upper_x) {
return false;
}
// 约束2: 满足所有朋友的时间要求
// a_i * (tc - x) + b_i * (tm - (S-x)) <= c_i
// (b_i - a_i) * x <= c_i - a_i*tc - b_i*tm + b_i*S
for (int i = 1; i <= n; ++i) {
// 使用 ll 处理中间计算,防止溢出
ll rhs = c[i] - a[i] * tc - b[i] * tm + b[i] * S;
if (a[i] < b[i]) {
// b_i - a_i > 0, x <= floor(rhs / (b_i - a_i))
// C++ 整数除法对于负数结果会向0取整需要特殊处理
ll diff = b[i] - a[i];
ll new_upper;
if (rhs >= 0) {
new_upper = rhs / diff;
} else {
// 等价于 floor(x/y) 对于 x<0, y>0
new_upper = (rhs - diff + 1) / diff;
}
upper_x = std::min(upper_x, new_upper);
} else if (a[i] > b[i]) {
// b_i - a_i < 0, x >= ceil(rhs / (b_i - a_i))
// 两边同乘 -1变为 (a_i - b_i) * x >= -rhs
// x >= ceil(-rhs / (a_i - b_i))
ll diff = a[i] - b[i];
ll neg_rhs = -rhs;
ll new_lower;
if (neg_rhs >= 0) {
// 等价于 ceil(x/y) 对于 x>=0, y>0
new_lower = (neg_rhs + diff - 1) / diff;
} else {
new_lower = neg_rhs / diff;
}
lower_x = std::max(lower_x, new_lower);
} else { // a[i] == b[i]
// x 的系数为 0不等式变为 0 <= rhs
if (rhs < 0) {
// 此不等式无解,说明该总花费 S 不可行
return false;
}
}
}
// 如果所有约束下的 x 的可行范围存在 (即下界 <= 上界),则该 S 可行
return lower_x <= upper_x;
}
sl void solve() {
std::cin >> n >> tc >> tm;
for (ll i = 1; i <= n; i++) {
std::cin >> a[i] >> b[i] >> c[i];
}
// 二分答案: 最小总花费
// 最小花费为 0, 最大花费为 tc+tm-2 (使制作时间都变为1)
ll left = 0, right = tc + tm - 2, ans = right + 1;
// 注意这里二分循环条件的修改
// [left, right] 是搜索区间
while (left <= right) {
ll mid = left + (right - left) / 2;
if (checkall(mid)) {
// 如果 mid 是一个可行的花费,我们记录它,并尝试寻找更小的花费
ans = mid;
right = mid - 1;
} else {
// 如果 mid 不可行,我们需要增加花费
left = mid + 1;
}
}
std::cout << ans << "\n";
}
int main() {
std::ios_base::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin >> T;
while (T--) {
solve();
}
}

50
src/10/20/P7556.cpp Normal file
View File

@ -0,0 +1,50 @@
#include <cstdint>
#include <cstdlib>
#include <iostream>
#include <istream>
#include <iterator>
#include <set>
#include <vector>
using ll = int64_t;
#define sl static inline
ll T,n,arr[10],ans;
std::set<ll >v;
sl void solve(){
ans=0;
std::cin>>n;
v.clear();
for(ll i=1;i<=n;i++){
std::cin>>arr[i];
v.emplace(arr[i]);
}
for(ll i=1;i<=n;i++){
for(ll j=i+1;j<=n;j++){
v.emplace(std::abs(arr[i]-arr[j]));
}
}
for(auto a = v.begin();a!=v.end();a++){
for(auto b=a;b!=v.end();b++){
for(auto c=b;c!=v.end();c++){
for(ll i=1;i<=n;i++){
if(!(arr[i]==*a || arr[i]==*b || arr[i]==*c || arr[i]==*a+*b||arr[i]==*a+*c||arr[i]==*b+*c||arr[i]==*a+*b+*c)){
goto nxt;
}
}
ans++;
nxt:;
}
}
}
std::cout<<ans<<"\n";
}
int main(){
std::iostream::sync_with_stdio(false);
std::cin.tie(nullptr);
std::cin>>T;
while(T--){
solve();
}
}