A. Lucky Year(Codeforces 808A)
思路
本题的入手点是,想明白一个正整数只有一个非零位是什么概念。
一个正整数只有一个非零位,那么这个数就只有最高位有非零位,也就可以表示成表示成这样:
a∗10b
,其中
a∈[1,9]
。
那么我们可以设计出这样的算法:将正整数
n
的最高位增加
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
string s;
ll k, d;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> s;
k = s[0] - '0' + 1;
for(int i = 1; i <= s.size() - 1; i++) {
k *= 10;
}
stringstream ss;
ss << s;
ss >> d;
cout << k - d << endl;
return 0;
}
B. Average Sleep Time(Codeforces 808B)
思路
本题的入手点在于,熟悉前缀和数组或能够构造出所需数列。
这道题要求的东西实际上是一个“奇怪的平均值”:“奇怪的和
s
”除以
当然,除了前缀和外,还可以直接计算答案。直接计算答案的思路是考虑数组中每个元素在
s
中的贡献度。我们可以先考察
现在根据规律尝试推广,于是尝试求推广后数列的通项公式设
f(n,k,i)
为数组长度为
n
,滑窗大小为
(以下代码为方法
2
的代码)
代码
#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 10;
int n, k, a;
double ans;
int main() {
ios::sync_with_stdio(false);
cout.tie(0);
cin.tie(0);
cin >> n >> k;
for(int i = 1; i <= n; i++) {
cin >> a;
ans += 1.0 * a * min(min(k, n - k + 1), min(i, n - i + 1));
}
cout << setprecision(7) << fixed;
cout << ans / (n - k + 1) << endl;
return 0;
}
C. Tea Party(Codeforces 808C)
思路
本题的入手点在于逐步地满足条件。
首先,我们一定可以给每个杯子倒上一半的水,如果不行的话就无解。那么接下来只剩下把水壶中的水全部倒出来这一个条件需要满足了。显然,可以通过贪心地从大杯子到小杯子依次将杯子倒满来满足条件。
代码
#include <bits/stdc++.h>
using namespace std;
typedef pair <int, int> p;
const int maxn = 110;
int n, w, a, idx, cur, tmp, ans[maxn];
p ps[maxn];
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n >> w;
for(int i = 1; i <= n; i++) {
cin >> a;
ps[i] = p(a, i);
}
sort(ps + 1, ps + n + 1);
for(int i = 1; i <= n; i++) {
a = ps[i].first;
idx = ps[i].second;
cur = max(cur, a / 2 + (a % 2 > 0));
ans[idx] = cur;
w -= cur;
if(w < 0) {
cout << -1 << endl;
return 0;
}
if(i == n && w > 0) {
ans[idx] += w;
}
}
for(int i = n; i >= 1; i--) {
a = ps[i].first;
idx = ps[i].second;
ans[idx] += tmp;
if(ans[idx] > a) {
tmp = ans[idx] - a;
ans[idx] = a;
}
else {
tmp = 0;
}
}
for(int i = 1; i <= n; i++) {
cout << ans[i] << ' ';
}
return 0;
}
D. Array Division(Codeforces 808D)
思路
本题的入手点在于考察将一个元素移动后会对结果有什么影响。
假设有下面的数组:
我们将其做一个划分(用竖线):
2234|5
显然,将 3 移动到竖线右边就能使得竖线左右两边的和相等。
可以观察到,将
3
从竖线左边移动到竖线右边,使得竖线左边的数字之和减少了
那么显然,如果已经知道了竖线的位置,同时分别知道了竖线左边的数字之和
s1
,也知道了竖线右边的数字之和
s2
,如果竖线左边存在数字
(s1−s2)/2
,或竖线右边存在数字
(s2−s1)/2
的话,就表示可以通过将元素右移或左移来使得竖线两边之和相等。
所以我们可以预处理出前缀和数组与后缀和数组,枚举竖线的位置的同时用
STL
的
set
维护竖线左右两边元素的位置,就可以为上面提到的算法高效地提供所有所需数据了。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5 + 10;
int n, a[maxn];
ll d, L[maxn], R[maxn];
set <ll> ls, rs;
int main() {
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
for(int i = 1; i <= n; i++) {
L[i] = L[i - 1] + a[i];
}
for(int i = n; i >= 1; i--) {
R[i] = R[i + 1] + a[i];
}
for(int i = 1; i <= n; i++) {
d = R[i + 1] - L[i];
if(d == 0 || d < 0 && d % 2 == 0 && ls.count(-d / 2)) {
cout << "YES" << endl;
return 0;
}
ls.insert(a[i]);
}
for(int i = n; i >= 1; i--) {
d = L[i - 1] - R[i];
if(d == 0 || d < 0 && d % 2 == 0 && rs.count(-d / 2)) {
cout << "YES" << endl;
return 0;
}
rs.insert(a[i]);
}
cout << "NO" << endl;
return 0;
}
E. Selling Souvenirs(Codeforces 808E)
思路
本题的入手点是,在动态规划中选用贪心策略。
虽然本题长得很像背包问题,但是因为物品数量和背包容量的乘积太大,所以不能用传统的方法来做。跟传统的问题相比,物品的种类只有三种(重量分别为
1,2,3
),这给了我们优化的契机。对于每种物品,我们显然会先拿价值比较高的。
因此我们可以将三种物品的价值分别放入数组
tab[1],tab[2],tab[3]
中,然后分别对这三个数组排序。用
tab[1],tab[2]
和动态规划预处理出
d[]
数组,其中
d[i]
表示只考虑重量为
1
和
那么可不可以在动态规划的过程中考虑三种物品而不是两种物品呢?答案是可以,但是要考虑的情况就要复杂一些,比如,当要向背包加入重量为
3
的物品时,有可能是加入三件重量为
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxm = 3e5 + 5;
vector <ll> tab[4];
int n, m, w, x, c[3][maxm];
ll ans, d[maxm], sum[maxm];
bool cmp(ll a, ll b) {
return a > b;
}
int main() {
ios::sync_with_stdio(false);
cout.tie(0);
cin.tie(0);
cin >> n >> m;
for(int i = 1; i <= n; i++) {
cin >> w >> x;
tab[w].push_back(x);
}
for(int i = 1; i <= 3; i++) {
sort(tab[i].begin(), tab[i].end(), cmp);
}
for(int j = 0; j < m; j++) {
sum[j + 1] = sum[j];
if(j < tab[3].size()) {
sum[j + 1] += tab[3][j];
}
}
if(tab[1].size() > 0) {
d[1] = tab[1][0];
c[1][1] = 1;
}
for(int i = 2; i <= m; i++) {
for(int j = 1; j <= 2; j++) {
if(c[j][i - j] >= tab[j].size()) {
continue;
}
if(d[i] < d[i - j] + tab[j][c[j][i - j]]) {
d[i] = d[i - j] + tab[j][c[j][i - j]];
c[j][i] = c[j][i - j] + 1;
c[j^3][i] = c[j^3][i - j];
}
}
}
for(int i = 0; i <= m; i++) {
ans = max(ans, d[i] + sum[(m - i) / 3]);
}
cout << ans << endl;
return 0;
}
(其它题目略)
本文提供了 Codeforces 808 比赛中的 A 至 E 题目的解题思路及代码实现,涉及算法包括贪心算法、前缀和、动态规划等。
995

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



