牛客小白月赛94
A - 小苯的九宫格
#include <bits/stdc++.h>
using namespace std;
int main(){
vector<int> a(11);
for(int i = 1; i <= 9; i ++) cin >> a[i];
string s;
cin >> s;
for(auto i : s)
cout << a[i - '0'];
return 0;
}
B - 小苯的好数组
如果原数组不是好数组,则原数组的任意子序列都一定不是好数组。所以如果原数组是好数组,原数组就是最长的子序列。
#include <bits/stdc++.h>
using namespace std;
using vi = vector<int>;
int main(){
ios::sync_with_stdio(false), cin.tie(nullptr);
int n;
cin >> n;
vi a(n);
for(auto & i : a) cin >> i;
if(is_sorted(a.begin(), a.end()))
cout << 0;
else
cout << n;
}
C - 小苯的数字合并
最优解一定是把前缀合并成一个或者把后缀合并成一个。所以可以提前前缀和预处理一下,然后枚举前缀或后缀的长度。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
#define int long long
using vi = vector<int>;
i32 main(){
ios::sync_with_stdio(false), cin.tie(nullptr);
int n;
cin >> n;
vi a(n);
for(auto & i : a) cin >> i;
vi preMax(n), preMin(n), pre(n);
preMax[0] = preMin[0] = pre[0] = a[0];
for(int i = 1; i < n; i ++) {
preMax[i] = max(preMax[i - 1], a[i]);
preMin[i] = min(preMin[i - 1], a[i]);
pre[i] = pre[i - 1] + a[i];
}
vi sufMax(n), sufMin(n), suf(n);
sufMax[n - 1] = sufMin[n - 1] = suf[n - 1] = a[n - 1];
for(int i = n - 2; i >= 0; i --) {
sufMax[i] = max(sufMax[i + 1], a[i]);
sufMin[i] = min(sufMin[i + 1], a[i]);
suf[i] = suf[i + 1] + a[i];
}
int res = sufMax[0] - sufMin[0];
for(int i = 1; i < n; i ++)
res = max(res, max(preMax[i - 1], suf[i]) - min(preMin[i - 1], suf[i]));
for(int i = n - 2; i >= 0; i --)
res = max(res, max(sufMax[i + 1], pre[i]) - min(sufMin[i + 1], pre[i]));
cout << res << "\n";
return 0;
}
D - 小苯的排列构造
首先,如果合法,则\(a_{i-1}\)一定是\(a_i\)的约数。
然后可以得到一个结论:如果若干个数\(x_j\)满足\(\gcd(a_{i-1},x_j) = a_i\),则第\(i\)位填谁没有影响。所以可以贪心的选择最小值,我们可以用双端队列来维护当前剩下了哪些数。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using vi = vector<int>;
i32 main(){
ios::sync_with_stdio(false), cin.tie(nullptr);
int n;
cin >> n;
vi a(n), p(n);
for(auto & i : a) cin >> i;
deque<int> q;
p[0] = a[0];
for(int i = 1; i <= n; i ++)
if(i != p[0]) q.push_back(i);
for(int i = 1; i < n; i ++) {
if(a[i-1] % a[i] != 0) {
cout << "-1\n";
return 0;
}
int fail = 0;
while(gcd(a[i - 1], q.front()) != a[i]) {
q.push_back(q.front()), q.pop_front();
fail ++;
if(fail > q.size()) {
cout << "-1\n";
return 0;
}
}
p[i] = q.front(), q.pop_front();
}
for(auto i : p) cout << i << " ";
return 0;
}
E/F - 小苯的01背包
这题和 普通背包的最大区别就是,普通背包选的物品越多,总体积和总价值一定递增,但是本题是递减。
考虑到依旧是求解最大价值,我们可以枚举价值然后求解最小体积。
我们枚举了价值\(s\),物品\(i\)能够被选择的条件是\((s \& a_i) = s\)。对于所有可以被选择的物品,我们一定是全选最优。
这样的话,对于easy版本,我们可以暴力的枚举价值就好了。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using vi = vector<int>;
const int N = (1 << 11) - 1;
i32 main(){
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, k;
cin >> n >> k;
vi v(n), w(n);
for(int i = 0; i < n; i ++)
cin >> v[i] >> w[i];
for(int i = N, tmp; i > 0; i --) {
tmp = N;
for(int j = 0; j < n; j ++)
if((i & w[j]) == i) tmp &= v[j];
if(tmp <= k) {
cout << i << "\n";
return 0;
}
}
cout << "0\n";
return 0;
}
对于hard 版本,我们无法再枚举价值,我么可以考虑试填法。
#include <bits/stdc++.h>
using namespace std;
using i32 = int32_t;
using vi = vector<int>;
const int N = (1 << 11) - 1;
i32 main(){
ios::sync_with_stdio(false), cin.tie(nullptr);
int n, k;
cin >> n >> k;
vi v(n), w(n);
for(int i = 0; i < n; i ++)
cin >> v[i] >> w[i];
int res = 0;
for(int i = 30; i >= 0; i --){
int tryRes = res | (1 << i), tmp = 0x7fffffff;
for(int j = 0; j < n; j ++)
if((tryRes & w[j]) == tryRes) tmp &= v[j];
if(tmp <= k) res = tryRes;
}
cout << res << "\n";
return 0;
}