比赛链接:

https://codeforces.com/contest/1680

C. Binary String

题目大意:

给定一个二进制字符串,可以从头部和尾部删除一个子串,最后的价值为删除的 1 的数量和剩下的 0 的数量的最大值,问这个价值最小是多少。

思路:

考虑暴力做法,枚举剩下的字符串的起点和终点,\(O(n^2)\),超时。
如果固定了左端点,可以发现右端点的移动对答案的影响是单调的,当右端点向右移动时,剩下的 0 的数量增加,删除的 1 的数量减少,向左移动时,剩下的 0 的数量减少,删除的 1 的数量增加。可以通过二分求答案。

也可以固定右端点,通过双指针移动,当左指针向右移动时,删除的 1 的数量增加,剩下的 0 的数量减少,右指针向右移动时,删除的 1 的数量减少,剩下的 0 的数量增加,当二者达到平衡的时候,就是值最小的时候。

二分

#include<bits/stdc++.h>
using namespace std;
int T;
string s;
void solve(){
	cin >> s;
	int n = s.size(), ans = n + 1, sum = 0;
	vector <int> cnt0(n + 1, 0), cnt1(n + 1, 0);
	for (int i = 0; i < n; i ++ ){
		cnt0[i + 1] = cnt0[i] + (s[i] == '0');
		cnt1[i + 1] = cnt1[i] + (s[i] == '1');
		sum += (s[i] == '1');
	}
	ans = min(ans, cnt1[n] - cnt1[0]);
	for (int i = 0; i < n; i ++ ){
		int l = i, r = n;
		while (l < r){
			int mid = l + r >> 1;
			int a = sum - (cnt1[mid + 1] - cnt1[i]), b = cnt0[mid + 1] - cnt0[i];
			if (a >= b){
				ans = min(ans, max(a, b));
				l = mid + 1;
			}
			else r = mid;
		}
	}
	cout << ans << "\n";
}
int main(){
	cin >> T;
	while (T -- )
		solve();
	return 0;
}

双指针

#include<bits/stdc++.h>
using namespace std;
int T;
string s;
void solve(){
	cin >> s;
	int n = s.size(), ans = n + 1, sum = 0;
	vector <int> cnt0(n + 1, 0), cnt1(n + 1, 0);
	for (int i = 0; i < n; i ++ ){
		cnt0[i + 1] = cnt0[i] + (s[i] == '0');
		cnt1[i + 1] = cnt1[i] + (s[i] == '1');
		sum += (s[i] == '1');
	}
	ans = min(ans, cnt1[n] - cnt1[0]);
	for (int l = 0, r = 0; r < n; ){
		while (l < r && sum - (cnt1[r + 1] - cnt1[l]) < cnt0[r + 1] - cnt0[l])
			l ++ ;
		ans = min(ans, max(sum - (cnt1[r + 1] - cnt1[l]), cnt0[r + 1] - cnt0[l]));
		r ++ ;
	}
	cout << ans << "\n";
}
int main(){
	cin >> T;
	while (T -- )
		solve();
	return 0;
}

E. Moving Chips

题意:

有一个 2 * \(n\) 的板,'.' 表示空,'*' 表示一个芯片,每次可以移动某个芯片向其他位置移动(不能移出板),当两个芯片移动到同一个格子的时候,一个芯片会消失,问最少几步可以让板中的芯片只剩下一个。

思路:

容易想到,肯定有一种方案可以让所有的芯片都移动到最右边的那个芯片,且操作步数最少,即没有后效性,所以可以想到 \(dp\)
定义 \(dp[i][j]\) 为将前 \(j\) 列的所有芯片都放到第 \(i\) 行的最少步数,容易得到转移方程。

代码:

#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve(){
	LL n;
	cin >> n;
	vector < vector<char> > s(2, vector<char> (n));
	for (int i = 0; i < 2; i ++ )
		for (int j = 0; j < n; j ++ )
			cin >> s[i][j];
	LL L = -1, R = 0;
	for (int j = 0; j < n; j ++ ){  //找到左右边界
		if (s[0][j] == '*' || s[1][j] == '*'){
			if (L == -1){
				L = j;
				R = j;
			}
			else{
				R = j;
			}
		}
	}
	vector < vector<LL> > dp(2, vector<LL> (n));
	dp[0][L] = (s[1][L] == '*');
	dp[1][L] = (s[0][L] == '*');
	for (int j = L + 1; j <= R; j ++ ){
		if (s[0][j] == '.' && s[1][j] == '.'){
			dp[0][j] = min(dp[0][j - 1] + 1, dp[1][j - 1] + 2);
			dp[1][j] = min(dp[0][j - 1] + 2, dp[1][j - 1] + 1);
		}
		else if (s[0][j] == '.' && s[1][j] == '*'){
			dp[0][j] = min(dp[0][j - 1] + 2, dp[1][j - 1] + 2);
			dp[1][j] = min(dp[0][j - 1] + 2, dp[1][j - 1] + 1);
		}
		else if (s[0][j] == '*' && s[1][j] == '.'){
			dp[0][j] = min(dp[0][j - 1] + 1, dp[1][j - 1] + 2);
			dp[1][j] = min(dp[0][j - 1] + 2, dp[1][j - 1] + 2);
		}
		else{
			dp[0][j] = min(dp[0][j - 1] + 2, dp[1][j - 1] + 2);
			dp[1][j] = min(dp[0][j - 1] + 2, dp[1][j - 1] + 2);
		}
	}
	cout << min(dp[0][R], dp[1][R]) << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL T;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}
posted on 2022-05-16 14:27  Hamine  阅读(118)  评论(1编辑  收藏  举报