牛客小白月賽94

PHarr發表於2024-05-28

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;
}