CF round 789 B2 - Tokitsukaze and Good 01-String (hard version)

Tokitsukaze and Good 01-String (hard version)

贪心

两个一组分成 \(\frac n2\) 个 01 段

  1. 当前这一组不相同,则操作数++,并且让这一组变成和上一组一样的,段数不变
  2. 当前这一组相同,如果和上一组相同,段数不变;如果不相同,则段数++。更新 last
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>

using namespace std;
typedef long long ll;

const int N = 2e5 + 10;
int n;
string s;
int main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while(T--)
	{
		cin >> n >> s;
		int x = 0, y = 0;
		char last = ' ';
		for (int i = 0; i < s.size(); i += 2)
		{
			if (s[i] != s[i+1])
				x++;
			else
			{
				if (last != s[i])
					y++;
				last = s[i];
			}
		}
		cout << x << " " << max(1, y) << endl;
	}
	return 0;
}

操作数贪心 + 段数DP

\(f[i][0/1]\) 为前 i 段,最后一段是 00/11 的最小段数

贪心:若某一段需要操作两次,那这个状态就是非法的,设为 INF

其他情况dp

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>

using namespace std;
typedef long long ll;

const int N = 2e5 + 10;
const int INF = 1e9;
int n;
string s;
int f[N][2];
int main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while(T--)
	{
		cin >> n >> s;
		s = " " + s;
		int x = 0;
        //初始化
		if (s[1] != s[2])
		{
			x++;
			f[2][0] = f[2][1] = 1;
		}
		else
		{
			if (s[1] == '1')
			{
                //贪心操作数,这一步不能操作两次,所以让这种状态不合法
				f[2][0] = INF;
				f[2][1] = 1;
			}
			else
			{
				f[2][0] = 1;
				f[2][1] = INF;
			}
		}
        
		for (int i = 4; i <= n; i += 2)
		{
			f[i][0] = f[i][1] = INF;
			if (s[i] != s[i-1])
			{
				x++;
				f[i][0] = min(f[i-2][0], f[i-2][1] + 1);
				f[i][1] = min(f[i-2][0] + 1, f[i-2][1]);
			}
			else
			{
                //贪心操作数,当前这一段是00,就不可能让这一段变成11,所以f[i][1]非法
				if (s[i] == '0')
					f[i][0] = min(f[i-2][0], f[i-2][1] + 1);
				else
					f[i][1] = min(f[i-2][0] + 1, f[i-2][1]);	
			}
		}
		cout << x << " " << min(f[n][0], f[n][1]) << endl;
	}
	return 0;
}

DP

\(f[i][0/1]\) 为前 i 段,最后一段是 00/11 的 {操作数,段数},首先满足操作数最小,再满足段数最小,所以可用 pair 存储

不考虑上一个方法中:贪心:若某一段需要操作两次,那这个状态就是非法的,设为 INF

若需要操作两次,就让他操作两次,最后取 min 时得到的也是操作数最小情况下段数最小的答案

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#include <cmath>
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<int, int> PII;
const int N = 2e5 + 10;
const int INF = 1e9;
int n;
string s;
PII f[N][2];

int main()
{
	ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
	int T;
	cin >> T;
	while(T--)
	{
		cin >> n >> s;
		s = " " + s;
		int x = 0;
		if (s[1] != s[2])
			f[2][0] = f[2][1] = {1, 1};
		else
		{
			if (s[1] == '1')
			{
				f[2][0] = {2, 1};
				f[2][1] = {0, 1};
			}
			else
			{
				f[2][0] = {0, 1};
				f[2][1] = {2, 1};
			}
		}
		
		for (int i = 4; i <= n; i++)
		{
			if (s[i] != s[i-1])
			{
				f[i][0] = min(PII(f[i-2][0].x + 1, f[i-2][0].y), PII(f[i-2][1].x + 1, f[i-2][1].y + 1));
				f[i][1] = min(PII(f[i-2][0].x + 1, f[i-2][0].y + 1), PII(f[i-2][1].x + 1, f[i-2][1].y));
			}
			else
			{
				if (s[i] == '0')
				{
					f[i][0] = min(PII(f[i-2][0].x, f[i-2][0].y), PII(f[i-2][1].x, f[i-2][1].y + 1));
					f[i][1] = min(PII(f[i-2][0].x + 2, f[i-2][0].y + 1), PII(f[i-2][1].x + 2, f[i-2][1].y));
				}
					
				else
				{
					f[i][0] = min(PII(f[i-2][0].x + 2, f[i-2][0].y), PII(f[i-2][1].x + 2, f[i-2][1].y + 1));
					f[i][1] = min(PII(f[i-2][0].x, f[i-2][0].y + 1), PII(f[i-2][1].x, f[i-2][1].y));
				}
			}
		}
		PII ans = min(f[n][0], f[n][1]);
		cout << ans.x << " " << ans.y << endl;
	}
	return 0;
}

posted @ 2022-05-12 20:03  hzy0227  阅读(70)  评论(0编辑  收藏  举报