码迷,mamicode.com
首页 > 其他好文 > 详细

CodeForces 1384C. String Transformation 1(贪心)(并查集)

时间:2020-07-26 23:12:06      阅读:78      评论:0      收藏:0      [点我收藏+]

标签:scanf   字母   策略   pac   put   false   lang   %s   amp   

题意:一个人有两个字符串A和B,两个字符串具有相同的长度n\((|A| = |B| = n)\),包含前20个小写字符(‘a‘到‘t‘)。每一次操作,这个人可以选择A字符串中字符相同的字母,然后从中选择一些位置,并把这些位置的字母变大。求字符串A变到字符串B的最少操作次数。

分析:我们可以贪心地进行操作,对于"aab"--->"bbc",我们可以先把"aa"变成"bb",那么"aab"--->"bbb",然后把第三个字母‘b‘变成‘c‘,那么‘aab‘--->‘bbc‘。我们可以通过从小到大枚举每个字母,检测字符串A和给定字母是否相同,然后求取对应位置的字母和这个字母相隔的差,在这些差中取最小值,意思是指对于"aabc"--->"bdec",如果枚举到了‘a‘字母,我们可以得到第一个字符串的第一个字母‘a‘和第二个字符串的第一个字母‘b‘的差1,第一个字符串的第二个字母‘a‘和第二个字符串的的第二个字母‘d‘之间的差3,对这个差取最小值,是我们每次要变化的值,对于所有的a,加上这个1,那么‘bbbc‘->‘bdec‘。我们可以通过这个贪心策略,得到最优解。每次枚举的一个字母,都可以把A字符串相同的字母消除掉。所以最多不超过20次。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include <algorithm>

using namespace std;
const int N = 100005;
const int inf = 0x3f3f3f3f;
char a[N], b[N];
int len;
int modify(int c)
{
	int mn = inf;
	for (int i = 1; i <= len; ++i)
	{
		if (a[i] == b[i]) continue;
		if (a[i] - ‘a‘ == c)
		{
			if (a[i] > b[i]) return -1;
			mn = min(mn, b[i] - a[i]);
		}
	}
	for (int i = 1; i <= len; ++i)
	{
		if (a[i] - ‘a‘ == c)
		{
			if (a[i] < b[i])
			{
				a[i] += mn;
			}
		}
	}
	//bool flag = true;
	if (mn == inf) return 0;
	return 1;
}
int main() 
{	
	int t;
	scanf("%d", &t);

	while (t--)
	{
		int n;
		scanf("%d", &n);

		scanf("%s%s", a + 1, b + 1);

		len = strlen(a + 1);

		bool flag = false;
		int res = 0;
		//枚举20个字符
		for (int i = 0; i < 20; ++i)
		{
			int g = modify(i);
			if (g == -1)
			{
				flag = true;
				break;
			}
			res += g;
		}
		if (!flag)
		{
			printf("%d\n", res);
		}
		else
		{
			printf("-1\n");
		}
	}


	return 0;
}

还有一种做法(并查集)
我们可以发现对于"aab"--->"bcc"来说,"aab"先变成"bbb",再变成"bcc","a"--->"b"--->"c",变换的关系具有传递性,我们可以联想到并查集,我们对于要变换的,从a的这个字母向b的这个字母连一条边,b字母向c字母连一条边,如下图技术图片,我们可以发现,对于上一题来说,我们用贪心的做法,都是先把相同的字母变到一个字母,然后再变到一个字母,我们每次的变换大小取决于对应位置的字母的最小差值。即并查集中边的条数,等价于连通块个数减-1,相当于2条边,2次操作即可。然后最后我们把所有的连通块里面的边数加起来即可了。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <set>
#include <algorithm>

using namespace std;
const int N = 100005;
char a[N], b[N];
int p[N], sz[N];

int find(int x)
{
	if (p[x] != x) p[x] = find(p[x]);
	return p[x];
}

void merge(int i, int j)
{
	int pa = find(i);
	int pb = find(j);
	if (pa != pb)
	{
		p[pa] = pb;
		sz[pb] += sz[pa];
	}
}

int main() 
{	
	int t;
	scanf("%d", &t);

	while (t--)
	{
		int n;
		scanf("%d", &n);

		scanf("%s%s", a + 1, b + 1);

		int len = strlen(a + 1);

		for (int i = 1; i <= 20; ++i) p[i] = i, sz[i] = 1;

		bool flag = true;
		for (int i = 1; i <= len; ++i)
		{
			if (a[i] != b[i])
			{
				if (a[i] > b[i])
				{
					flag = false;
					break;
				}
				merge(a[i] - ‘a‘ + 1, b[i] - ‘a‘ + 1);
			}
		}

		if (!flag)
		{
			puts("-1");
		}
		else
		{
			int res = 0;
			for (int i = 1; i <= 20; ++i)
			{
				int p = find(i);
				if (p == i)
				{
					res += sz[i] - 1;
				}
			}

			printf("%d\n", res);
		}
	}


	return 0;
}

CodeForces 1384C. String Transformation 1(贪心)(并查集)

标签:scanf   字母   策略   pac   put   false   lang   %s   amp   

原文地址:https://www.cnblogs.com/pixel-Teee/p/13381800.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!