前言:
百度之星选拔赛。
题目难度中规中矩。
前5道没什么太大难度,代码量很小,主要看你平时练习状态,摸鱼的一下就看出来了。
大一同学预计大部分出4、5题,少部分能出6题,大二同学预计6~7题,可能有AK。
题目详情:
题号
类型
思维
代码
A
签到
0
1
B
贪心
0
2
C
贪心STL/双指针
0
2
D
递推/简单DP
1
1
E
数学/推式子
2
1
F
二分
2
3
G
状压DP/二分图
3
3/2
H
二分图/最大流
3
2/3
问题 A: 来点鸡肉堡
签到题,注意给出的
是总人数即可。
#include <bits/stdc++.h> using namespace std; #define int long long typedef long long ll; typedef pair<int, int> pii; const int N = 1e5 + 10, mod = 1e9 + 7; void solve() { int n, x, a, b; cin >> n >> x >> a >> b; cout << x * b + (n - x) * a << "\n"; } signed main() { ios::sync_with_stdio(0); cin.tie(0); int t; cin >> t; while (t--) solve(); return 0; }
问题 B: 数组询问
很明显的贪心
1.循环一遍,sum记录非0的情况下的和,cnt统计0的数量。
2.最小值就是0的数量 * 区间左值(cnt*l),反之最大值就是0的数量*区间右值(cnt*r)。
3.答案就是最小:sum+cnt*l,最大:sum+cnt*r;
#include <bits/stdc++.h> using namespace std; #define int long long typedef long long ll; typedef pair<int, int> pii; const int N = 1e5 + 10, mod = 1e9 + 7; void solve() { int n, m; cin >> n >> m; vector<int> a(n + 1); int sum = 0, cnt = 0; for (int i = 1; i <= n; i++) { cin >> a[i]; sum += a[i]; if (!a[i]) cnt++; } while (m--) { int l, r; cin >> l >> r; cout << sum + cnt * l << " " << sum + cnt * r << "\n"; } } signed main() { ios::sync_with_stdio(0); cin.tie(0); int t; t = 1; while (t--) solve(); return 0; }
问题 C: 选数乘积
贪心,数据保证
是正整数,尽可能每次乘最大的数。
但数据范围到1e9,开数组统计
有没有被删显然是做不到的。
可以考虑用set/map统计,
或者使用双指针判断a[i]和a[i+1]是否相同,根据你的排序,特判首部或尾部即可。
#include <bits/stdc++.h> using namespace std; #define int long long typedef long long ll; typedef pair<int, int> pii; const int N = 1e5 + 10, mod = 1e9 + 7; void solve() { int x, y; cin >> x >> y; int n; cin >> n; vector<int> a; set<int> q; for (int i = 1; i <= n; i++) { int x; cin >> x; if (q.count(x)) continue; a.push_back(x); q.insert(x); } sort(a.begin(), a.end(), greater<int>()); if (x >= y) { cout << 0 << "\n"; return; } for (int i = 0; i < a.size(); i++) { x *= a[i]; if (x >= y) { cout << i + 1 << "\n"; return; } } cout << -1 << "\n"; } signed main() { ios::sync_with_stdio(0); cin.tie(0); int t; t = 1; while (t--) solve(); return 0; }
问题 D: 上台阶
一眼下去很典的递推。斐波那契数列
。
但考虑到有台阶不能走,并且一次只能上1或2层台阶。
所以一旦有两个连续的台阶不能走,那么答案为0。
若
无法走,则
无法通过
到达,无需添加
的方案数。
对于
同理。
不要忘记取模!
#include <bits/stdc++.h> using namespace std; #define int long long typedef long long ll; typedef pair<int, int> pii; const int N = 1e5 + 10, mod = 1e9 + 7; int f[N];//表示从第0层走到第i层的方案数 bool st[N];//判断第i个台阶能不能走,0表示能走,1表示不能走。 void solve() { int n, m; cin >> n >> m; for (int i = 1; i <= m; i++) { int x; cin >> x; st[x] = 1; } f[0] = 1; for (int i = 1; i <= n; i++) { if (!st[i] && !st[i - 1])//判断第i个台阶和第i-1个台阶是否可以走 f[i] += f[i - 1]; if (i > 1 && !st[i] && !st[i - 2]) f[i] += f[i - 2]; f[i] %= mod;//别忘记取模 } cout << f[n]; } signed main() { ios::sync_with_stdio(0); cin.tie(0); int t; t = 1; while (t--) solve(); return 0; }
问题 E: 简单数学问题
一道CCPC的原,很简单的推式子,没写出来的拿演草纸推一下。
#include <bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int, int> pii; #define int long long const int N = 1e5 + 10, mod = 1e9 + 7; void solve() { int x, y; cin >> x >> y; //lcm(a,b)=a*b/gcd(a,b),不知道的可以学习基础数论 cout << 1 << " " << (x * y / __gcd(x, y)) / __gcd(x, y) << "\n"; } signed main() { ios::sync_with_stdio(0); cin.tie(0); int t; cin >> t; while (t--) solve(); return 0; }
问题 F: 整数反应
二分+贪心,2024江苏CCPC,
为了减少查询时间,所以要保证每次放入数据后,序列是有序的,并且数据可以重复,考虑使用multiset模拟插入删除的过程。
multiset自带lower_bound二分查找函数。
#include <bits/stdc++.h> using namespace std; #define int long long typedef long long ll; typedef pair<int, int> pii; const int N = 1e5 + 10, mod = 1e9 + 7; void solve() { int n; cin >> n; vector<int> b(n + 1), a(n + 1); for (int i = 1; i <= n; i++)cin >> a[i]; for (int i = 1; i <= n; i++)cin >> b[i]; int l = 1, r = 1e9; auto check = [&](int mid) -> bool { multiset<int> q1, q2; for (int i = 1; i <= n; i++) { if (b[i] == 1) { if (q2.empty()) { q1.insert(a[i]); } else { auto pos = q2.lower_bound(mid - a[i]); if (pos == q2.end()) { return 0; } else { q2.erase(pos); } } } else { if (q1.empty()) { q2.insert(a[i]); } else { auto pos = q1.lower_bound(mid - a[i]); if (pos == q1.end()) { return 0; } else { q1.erase(pos); } } } } return 1; }; while (l < r) { int mid = l + r + 1 >> 1; if (check(mid)) l = mid; else r = mid - 1; } cout << l << "\n"; } signed main() { ios::sync_with_stdio(0); cin.tie(0); int t; t = 1; while (t--) solve(); return 0; }
问题 G/F: 切蛋糕(easy/hard)
本质上是一道二分图最大匹配的问题。
二分图板子题,难在你能看出他是个二分图。
不会二分图的,可以去bilibili或者acwing的算法基础课进行学习。
G的数据范围比较小,n只有11,所以可以使用状态压缩DP求解。
与蒙德里安的梦想类似,时间复杂度
约为4e7。
下面给出二分图做法:
考虑一个格子
为偶数:不妨记这样的格子为白格子.
为奇数:不妨记这样的格子为黑格子.
将所有的偶数染成白色,所有奇数染成黑色(进行二染色)。
最后我们可以发现,每一个方块的四周,都不会有和它一样颜色的方块,也就是一块蛋糕不可能覆盖相同颜色的两个点(同一集合中不会有边)。
那我们将值为偶数的方块放到一个集合中,值为奇数的方块放到一个集合中,构造一个二分图,把题目中的蛋糕看作连接两个方块的一条边,切最多块蛋糕就可以看作找到最多的边。
最多O(n^2)个点,O(n^2)条边,所以时间复杂度O(n^4)。
#include <bits/stdc++.h> using namespace std; #define int long long typedef long long ll; typedef pair<int, int> pii; const int N = 110, mod = 1e9 + 7; int n, t; bool st[N][N], g[N][N]; pii match[N][N]; int dx[] = {0, 1, 0, -1}; int dy[] = {1, 0, -1, 0}; bool find(int xx, int yy) { for (int i = 0; i < 4; i++) { int x = dx[i] + xx; int y = dy[i] + yy; if (x < 1 || x > n || y < 1 || y > n || g[x][y]) continue; if (!st[x][y]) { st[x][y] = 1; auto [lx, ly] = match[x][y]; if (lx == 0 || find(lx, ly)) { match[x][y] = {xx, yy}; return 1; } } } return 0; } void solve() { cin >> n >> t; int ans = 0; for (int i = 1; i <= t; i++) { int x, y; cin >> x >> y; g[x][y] = 1; } for (int i = 1; i <= n; i++) { for (int j = 1; j <= n; j++) { if ((i + j) % 2 == 0 && !g[i][j]) { memset(st, 0, sizeof st); if (find(i, j)) ans++; } } } cout << ans << "\n"; } signed main() { ios::sync_with_stdio(0); cin.tie(0); int t; t = 1; while (t--) solve(); return 0; }
本题还有最大流做法:
通过横纵坐标之和的奇偶性,将整张图划分为二分图,将合法的奇数点与 源点 S 相连, 将合法的偶数点与汇点 T 相连,由于每个点只能用一次,所以每条边的容量为 1 , 然后将奇数点和其相邻的合法偶数点相连,跑一边最大流算法就行了




974

被折叠的 条评论
为什么被折叠?



