CF1400:1560D、1245C

1560D. Make a Power of Two(思维,双指针)

题意:

给定一个数n,问最少需要多少次操作,能够将其变为2的幂次数。(n ≤1e9)
1.选择一个数位,删掉。
2.在最后面增加一个数位,为任意值。

思路:

仅仅由所给数n来判断最终能变成2的那么次幂数是很难的。
但是2的次幂数一共就60个,所以可以遍历所有的幂次数x,判断当前值变为x所需要的最小操作数。取所有数答案的最小值。

那么数n变为幂次数x最小需要操作多少次呢?
由于增加一个数位操作只能增加到最后一位,所以数n只能对幂次数前面的数位有贡献,然后在后面补充剩下的,构成幂次数x。(贡献的前面数位必须是连续的。如果中间有间隔,只能在后面添加,那么无论如何都是不能加上去的)
所以就要看数n最多能对幂次数前面多少数位有贡献了。

遍历幂次数的每一位,看这一位能不能贡献到,如果能,继续往后走;如果不能,就说明这一位无法贡献,停止。
双指针。

找到最大贡献值cnt。那么数n的其他数位要删掉,幂次数x后面的数位要补上。
所以需要的操作数为 len1+len2-2*cnt

Code:

const int N=100010;
int n,m,T;
string a,b,s[N];

int main(){
    
    
	Ios;
	
	ll x=1;
	for(int i=0;i<62;i++)
	{
    
    
		if(i) x*=2;
		s[i+1]=to_string(x);
	}
	
	cin>>T;
	while(T--)
	{
    
    
		cin>>a;
		int ans=1e9;
		
		for(int k=1;k<=62;k++)
		{
    
    
			string x=s[k];
			int j=0,cnt=0;
			for(int i=0;i<x.size();i++)
			{
    
    
				while(j<a.size()-1&&x[i]!=a[j]) j++;
				if(x[i]==a[j]) j++,cnt++;
				else break;
			}
			
			int t=x.size()+a.size()-2*cnt;
			ans=min(ans,t);
		}
		cout<<ans<<endl;
	}
	return 0;
}

扩展:

如果可以将任意位置删除,可以从任意位置添加,问将一个数变为2的幂次数需要的最小操作数?
这就可以将原串和所有2的幂次数分别求最长公共子序列,取操作数最小的。

const int N = 200010, mod=1e9 + 7;
int T, n, m;
string s[N];
int f[50][50];

int main(){
    
    
	ll x=1;
	for(int i=0;i<31;i++){
    
    
		if(i) x*=2;
		s[i+1]=to_string(x);
	}
	
	cin>>T;
	while(T--)
	{
    
    
		string a;cin>>a;
		n=a.size();
		a=" "+a;
		
		int ans=1e9;
		for(int k=1;k<=31;k++)
		{
    
    
			string x=" "+s[k];
			int m=x.size()-1;
			mem(f,0);
			
			for(int i=1;i<=n;i++)
			{
    
    
				for(int j=1;j<=m;j++)
				{
    
    
					f[i][j]=max(f[i][j],f[i-1][j]);
					f[i][j]=max(f[i][j],f[i][j-1]);
					if(a[i]==x[j]) f[i][j]=max(f[i][j],f[i-1][j-1]+1);
				}
			}
			ans=min(ans,n+m-2*f[n][m]);
		}
		cout<<ans<<endl;
	}
	
	return 0;
}

1245C. Constanze’s Machine(组合,斐波拉契数)

题意:

给定一个修改过的字符串,问原串有多少种?
原串中的字符m可能被修改为nn,w可能被修改为uu。

思路:

可以由若干个 nnn…能够还原为m的方式数和uuu…能够还原为w的方式数 相乘。组合原理。
那么nnn…能够还原为m的方式数为多少呢?
有一对n变成m的情况,两对n变成m的情况,三对…
如果有两对n变为m,那么总个数为n-2,还原出的串个数为: A n − 2 n − 2 / ( A 2 2 ∗ A n − 4 n − 4 ) A_{n-2}^{n-2}/(A_2^2*A_{n-4}^{n-4}) An2n2/(A22An4n4)
其规律是这样的:1,2,3,5,8,13,21…斐波拉契数列。

Code:

const int N = 200010, mod=1e9 + 7;
int T, n, m, a[N];
int f[N];

void pre(){
    
    
	f[1]=1,f[2]=2,f[3]=3;
	for(int i=4;i<=1e5;i++) f[i]=(f[i-1]+f[i-2])%mod;
}

int main(){
    
    
	Ios;
	
	string s;
	cin>>s;
	n=s.size();
	s=" "+s;
	
	int flag=0;
	for(int i=1;i<=n;i++) if(s[i]=='m'||s[i]=='w') flag=1;
	if(flag){
    
    
		cout<<0;return 0;
	}

	pre();
	ll ans=1,cnt=0;
	for(int i=0;i<s.size();i++)
	{
    
    
		if(s[i]=='u')
		{
    
    
			cnt=0;
			int j;
			for(j=i;j<s.size();j++)
			{
    
    
				if(s[j]!='u') break;
				cnt++;
			}
			i=j-1;
			ans=ans*f[cnt]%mod;
		}
		else if(s[i]=='n')
		{
    
    
			cnt=0;
			int j;
			for(j=i;j<s.size();j++)
			{
    
    
				if(s[j]!='n') break;
				cnt++;
			}
			i=j-1;
			ans=ans*f[cnt]%mod;
		}
	}
	cout<<ans;
	return 0;
}

也可以用dp实现:
f[i]表示,前i个位置可以转化为原串的方案数。
初始值f[0]设为1.
如果当前位置可以和前一位置合并的话,f[i] = f[i-2]+f[i-1],可以和前一位置合并,那么方案数为f[i-2],也可不合并,方案数为f[i-1]。

const int N = 200010, mod=1e9 + 7;
int T, n, m, a[N];
int f[N];

int main(){
    
    
	Ios;
	
	string s;
	cin>>s;
	n=s.size();
	s=" "+s;
	
	int flag=0;
	for(int i=1;i<=n;i++) if(s[i]=='m'||s[i]=='w') flag=1;
	if(flag){
    
    
		cout<<0;return 0;
	}
	
	f[0]=1;
	for(int i=1;i<=n;i++)
	{
    
    
		f[i]=f[i-1];
		if(s[i]=='u'&&s[i-1]=='u'||s[i]=='n'&&s[i-1]=='n')
			f[i]=(f[i-1]+f[i-2])%mod;
	}
	cout<<f[n];
	return 0;
}

猜你喜欢

转载自blog.csdn.net/Mr_dimple/article/details/121043007
今日推荐