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;
    }
    
  • 相关阅读:
    Git failed with a fatal error. Authentication failed
    HttpClient 获取json,即使返回状态不成功也返回json
    win10恢复Windows Media Player
    .NET Core部署到linux(CentOS)最全解决方案
    EasyNetQ 相关问题和解决方法
    RabbitMQ And EasyNetQ
    打爆你的 CPU
    通过代码实现 OutOfMemory
    如何写一段死锁代码
    docker 容器(container)使用ssh服务登录一段时间无操作后自动断开问题解决
  • 原文地址:https://www.cnblogs.com/pixel-Teee/p/13381800.html
Copyright © 2011-2022 走看看