1. 程式人生 > 其它 >CF round 789 B2 - Tokitsukaze and Good 01-String (hard version)

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