zoukankan      html  css  js  c++  java
  • CodeForces 1384C. String Transformation 1(贪心)(并查集)

    题意:一个人有两个字符串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
    ", res);
    		}
    		else
    		{
    			printf("-1
    ");
    		}
    	}
    
    
    	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
    ", res);
    		}
    	}
    
    
    	return 0;
    }
    
  • 相关阅读:
    Codeforces Gym 100571A A. Cursed Query 离线
    codeforces Gym 100500 J. Bye Bye Russia
    codeforces Gym 100500H H. ICPC Quest 水题
    codeforces Gym 100500H A. Potion of Immortality 简单DP
    Codeforces Gym 100500F Problem F. Door Lock 二分
    codeforces Gym 100500C D.Hall of Fame 排序
    spring data jpa 创建方法名进行简单查询
    Spring集成JPA提示Not an managed type
    hibernate配置文件中的catalog属性
    SonarLint插件的安装与使用
  • 原文地址:https://www.cnblogs.com/pixel-Teee/p/13381800.html
Copyright © 2011-2022 走看看