牛客小白月赛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;
}
posted @ 2024-05-28 17:43  PHarr  阅读(11)  评论(0编辑  收藏  举报