Educational Codeforces Round 154 (Rated for Div. 2)(A~D)

连续上分咯

1861A - Prime Deletion 

        题意:给定一个字符串,包含了1~9数字各一个,你可以删除最多7个数字,要求删完以后的字符串组成的数字为素数。

        思路:13与31都是素数,因此只需要看1在3的前面还是后面再决定输出13还是31即可。

        

void solve() 
{
	string s;
	cin>>s;
	map<int,int>mp;
	for(int i = 0 ; i <= 8 ; i ++){
		int x = s[i] - '0';
		mp[x] = i;
	}
	if(mp[3] < mp[1]){
		cout<<31<<endl;
	}
	else{
		cout<<13<<endl;
	}
}  

 1861B - Two Binary Strings 

        题意:给定两个01串,其中第一个字符是0,最后一个字符是1。你可以对字符串进行如下操作:选定两个相同的字符,将字符串中这两个字符中间的字符变成选中的字符。求最终两个字符串能否相同。

        思路:因为头是0,尾是1。所以只需要在字符串中选择一段连续的01,然后0左边全部变为0,1右边全部变为1即可。若两个字符串中有相同位置的01,那么他们最终就能相同。

void solve() 
{
	string s1 , s2;
	cin>>s1>>s2;
	int len = s1.size();
	for(int i = 1 ; i < len ; i ++){
		if(s1[i - 1] == '0' && s2[i - 1] == '0' && s1[i] =='1' && s2[i] =='1'){
			cout<<"YES\n";
			return;
		}
	}
	cout<<"NO\n";
}  

1861C - Queries for the Array

        题意:起初有一个空的栈,接下来给出一段字符串,其中按顺序‘+’代表了一个数入栈,‘-’代表了一个数出栈,‘0’代表了栈是非递增的,‘1’代表了栈是递增的。令栈中元素数量少于2的情况默认为递增。求给出的字符串能否满足条件。

        解释:例如字符串“+ + 0 - 1”,代表了先两个数入栈,然后整个栈非递增,然后再出栈一个数,最后栈是递增的。先入 1 ,再入0,此时栈是非递增的。然后0出栈,栈中只剩下1个元素,为递增。所以该字符串能够满足条件。

        思路:这是一道反悔贪心的题目,我们将相邻两个数字之间的操作看成一个过程,总共有4种可能:1、 1xxx1的情况,此时无论中间是怎么样的,都一定能够满足递增条件。2、1xxx0的情况,此时我们必须要保证栈上能够新增至少一个数,才能保证变为非递增的。3、0xxx1的情况,此时我们必须要满足在整个过程当中,将前面的非递增的数减去才能满足条件。4、0xxx0的情况,这种情况最为复杂:若是0 - - - 0,那么必须要保证栈中倒数第四个数是非递增的。而这里便是需要我们反悔的地方了。

        怎么贪心?假设 x 为我们需要将末尾第x个数删掉之后才能变得递增。我们在0xxx0 与 1xxx0的情况之下,都尽可能的满足 x = 1 , 特殊情况: 0+++0 的情况,那么 x = x + 3了。而处理0 - - - 0的情况,我们还需要将 x 增大。因此我们还需要假设 y 为 x 的最大取值。在 1 xxx 0 的情况之下,y的值为最终整个过程中加了几个数上去。在0 xxx 0 的情况之下,y  先需要减去整个过程当中删了前面多少个数,然后再加上整个过程当中加了几个数。处理0 - - - 0 时,若y < 3 则代表删了3个数以后必然是递增的,那么便无法满足了。

        

void solve() 
{
	string s;
	cin>>s;
	int jinjian = 0 , jinjia = 0 , jian = 0 , jia = 0 ;
	int t_max = 0;//最多有连续多少个数是递减的
	int t_min = 1;
	int cnt = 0;
	int len = s.size();
	int flag = 1;
	for(int i = 0 ; i < len ; i ++){
		if(s[i] == '+'){
			jia++;
			jinjia = max(0 , jinjia + 1);
			jinjian = max(jinjian , jian - jia);			
			cnt++;
		}
		if(s[i] == '-'){
			jian++;
			jinjia = max(0 , jinjia - 1);
			jinjian = max(jinjian , jian - jia);
			cnt--;
		}
		if(s[i] == '1'){
			if(flag == 0){
				if(jinjian < t_min){
					cout<<"NO\n";
					return;
				}
			}
			t_max = 0;
			t_min = 0;
			jia = 0;
			jian = 0;
			jinjia = 0;
			jinjian = 0;
			flag = 1;
		}
		if(s[i] == '0'){
			if(cnt == 0 || cnt == 1){
				cout<<"NO\n";
				return;
			}
			if(flag == 1){
				if(jinjia == 0){
					cout<<"NO\n";
					return;
				}
				else{
					t_max = jinjia;//加的几个数全是递减
					t_min = 1;
				}
			}
			else{
				if(jinjia == 0){
					if(t_max <= jinjian){
						cout<<"NO\n";
						return;
					}
				}
				else{
					t_max = max(0 , t_max - jinjian);
					t_max += jinjia;
					if(jinjian == 0){
						t_min += (jia - jian);
					}
					else{
						t_min = 1;
					}
				}
			}
			jian = 0;
			jia = 0;
			jinjia = 0;
			jinjian = 0;
			flag = 0;
		}
	}
	cout<<"YES\n";
}    

1861D - Sorting By Multiplication 

        题意:给定一个数组,每次操作可以选择 l ,r , x 。令a_{i} = a_{i} * x (l \leq i\leq r)。求让整个数组严格递增的最小操作数。

        思路:一道最小操作数的dp题,因为x可以任选,因此 a_{i} 可以分为三种情况:1、负无穷。2、原数。3、正无穷。因此假设dp[i][0]代表了前i个数,且a_{i}为负无穷情况下,整个数组严格递增的最小操作数。dp[i][1]代表了前i个数,且a_{i}不变情况下,整个数组严格递增的最小操作数。dp[i][2]代表了前i个数,且a_{i}为正无穷下,整个数组严格递增的最小操作数。于是有状态转移方程:

假设a[i] > a[i - 1]:dp[i][0] = dp[i - 1][0] + 1 \\dp[i][1] = min(dp[i - 1][1] , dp[i - 1][0])\\ dp[i][2] = min(min(dp[i - 1][1],dp[i - 1][0]) + 1 , dp[i - 1][2])

此时如果要将 a_{i}变为负无穷且大于 a_{i-1} ,不能和 a_{i-1}  乘同一个系数,因此需要操作数 + 1。

如果要将 a_{i} 不变,那么直接从a_{i-1} = -\infty or a_{i -1} = a_{i - 1}转移过来即可。

如果要将 a_{i} 变成正无穷,那么和 a_{i-1}乘同一个系数即可。或者自己再单独乘一个系数(操作数 + 1)

反之a[i] < a[i - 1] :dp[i][0] = dp[i - 1][0]\\dp[i][1] = dp[i - 1][0]\\ dp[i][2] = min(min(dp[i - 1][1],dp[i - 1][0]) , dp[i - 1][2]) + 1

 

此时如果要将 a_{i}变为负无穷且大于 a_{i-1} ,和 a_{i-1}  乘同一个系数即可。

如果要将 a_{i} 不变,那么直接从a_{i-1} = -\infty转移过来即可。

如果要将 a_{i} 变成正无穷,需要自己单独乘一个系数,操作数+1。

最后输出第n位,a_{n}为负无穷,原数,正无穷的最小操作数。

void solve() 
{
	int n;
	cin>>n;
	int dp[n + 5][3];//-无穷,不变,正无穷
	memset(dp , 0x3f , sizeof dp);
	int a[n];
	dp[0][0] = 1;
	dp[0][1] = 0;
	dp[0][2] = 1;
	for(int i = 0 ; i < n ; i ++){
		cin>>a[i];
	}
	for(int i = 1 ;i < n ; i ++){
		if(a[i] < a[i - 1]){
			dp[i][0] = dp[i - 1][0];
		}
		else{
			dp[i][0] = dp[i - 1][0] + 1;
		}
		if(a[i] > a[i - 1]){
			dp[i][1] = min(dp[i - 1][1] , dp[i - 1][0]);
		}
		else{
			dp[i][1] = dp[i - 1][0];
		}
		if(a[i] > a[i - 1]){
			dp[i][2] = min(dp[i - 1][1] + 1 , min(dp[i - 1][2] , dp[i - 1][0] + 1));
		}
		else{
			dp[i][2] = min(dp[i - 1][1] + 1 , min(dp[i - 1][2] + 1 , dp[i - 1][0] + 1));
		}
	}
	int ans = 1e7;
	for(int i = 0 ; i < 3; i ++){
		ans = min(ans , dp[n - 1][i]);
	}
	cout<<ans<<endl;
}            

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值