Educational Codeforces Round 153 (Rated for Div. 2)

发布时间 2023-08-18 22:34:20作者: du463

Educational Codeforces Round 153 (Rated for Div. 2)

这次的div2有点难度,当时b题思路对了,但是没有写好

A题传送门

A题意:

给你一个只包含'('和')'的字符串,要求你将他的长度扩大一倍,并且使得所有括号匹配且组成的序列当中不能存在原序列的子序列

A思路:

这道题一开始写的时候没有注意题意,忽略了序列应该与原序列没有匹配的条件,所以一开始想简单了,理解后,事实上我们只存在两种序列形式,一种是(((,))这种一次出现多个相同的,一种是)(一直是先出现右括号,再出现左括号的,这里事没有一次出现多个相同的,我们只需要改变原序列中出现这两种情况的地方就行,针对第一种情况,我们可以()()()用这个方式改变,针对第二种情况,我们可以用((()))这种方式改变。注意一个特殊情况()这个一定是子序列

A代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve(){
    string s;
    cin>>s;
    // cout<<s<<endl;
    int n=s.size();

    if(s=="()"){
        cout<<"NO"<<endl;
        return ;
    }
    cout<<"YES"<<endl;
    int flag=0;
    for(int i=0;i<n;){
        int ans=1;
        int j=i+1;
        while(s[j]==s[i]){
            ans++;
            j++;
        }
        flag=max(flag,ans);
        i=j;
    }
    if(flag==1){
        for(int i=0;i<n;i++){
            cout<<"(";
        }
        for(int i=0;i<n;i++){
            cout<<")";
        }
    }
    else{
        for(int i=0;i<n;i++){
            cout<<"()";
        }
    }
    cout<<endl;
    
}
int main(){
    int t;
    cin>>t;
    while(t--){
        solve();

    }
    return 0;
}

B题传送门

B题意:

一开始你有a1枚一元硬币,ak枚k元硬币和无限多枚特殊硬币(可以当成一元或者是k元),请使用最少数量的特殊硬币来恰好实现m的价值

B思路:

也许就是一个贪心的思路,我们首先用k元硬币补充,再考虑一元硬币,如果一元硬币的数量和k元硬币的数量够用,那就不需要使用特殊硬币,即使需要特殊硬币,我们也应该首先考虑价值为k的特殊硬币

B代码:

#include<bits/stdc++.h>
using namespace std;
void solve(){
	int m,k,a1,ak;
	cin>>m>>k>>a1>>ak;
	m-=min(m/k,ak)*k;
	if(m<=a1){
		cout<<0<<endl;
	}
	else{
		int ans=(m-a1)/k+(m-a1)%k;
		if(m>=((m-a1)/k+1)*k){
			ans=min(ans,(m-a1)/k+1);
		}
		cout<<ans<<endl;

	}

}
int main(){
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

C题传送门

C题意:

这是一个博弈游戏,给定一个长度为n的序列,有两位玩家Alice和Bob,Alice先手,第一步将筹码放在任意一个位置上,接下来每一个人都需要将筹码放到当前筹码所在元素的左边并且小于当前筹码所在的元素,直到一人无法操作,另一人即可获胜,求Alice第一步位置的个数

C思路:

我们首先考虑必败的情况,1.当Alice选择的数没有比Bob更小的了,2.当Alice选择的数前面存在一个最小数,比Alice当前数小。其余的情况都是必胜的
我们可以从左往右考虑,假如Alice在i点放了筹码,那么考虑i点前面是否有可选择的必胜状态,如果存在必胜状态,或者是没有可以选择的状态(即i点就是最小值),那么i就是必败状态,我们可以设置两个变量a,b表示i前面的最小值和必胜状态的最小值,如果大于a表示前面有可选状态,大于b表示前面有可选的必胜状态。(如果Alice想赢的话,放的i点前面不能有必胜状态)

C代码:

#include<bits/stdc++.h>
using namespace std;
void solve(){
	int n;
	cin>>n;
	std::vector<int> v(n+1);
	for(int i=1;i<=n;i++){
		cin>>v[i];
	}
	int ans=0;
	int a=v[1];
	int b=1e9;//i前面的必胜状态最小值
	for(int i=2;i<=n;i++){
		if(v[i]>a&&v[i]<b){//有可选状态但是没有必胜状态
			b=min(b,v[i]);
			ans++;
		}
		a=min(a,v[i]);//a表示i前面的最小值,这里是为下一次更新

	}
	cout<<ans<<endl;

}
int main(){
	int t;
	cin>>t;
	while(t--){
		solve();
	}
	return 0;
}

D题传送门

D题意:

给定一个01串s ,每次操作可以交换任意两个数。求当整个串的‘01’和‘10’子序列(可以不连续)相等时的最小操作数。

D思路:

这个看了网上一个思路就豁然开朗,原来是忘记设计偏移量了!!
学习地点
直接考虑dp[ i ] [ j ] [ k ],其中 i 表示前 i 位 , j 表示前 i 位中有多少个 1 , k 表示 10 比 01 子序列多多少个(由于10可能会比01数量少,因此需要有个偏移值,这边直接设偏移值为3000),dp值表示为01翻转的操作数。当第 i 位数为 0 时,10子序列会增加 j 个 , 同理,当第 i 位数为 1 时,01子序列会增加(i - j) 个 。于是得到状态转移方程:

\[dp[i][j+1][k-cnt0]=min(dp[i][j+1][k-cnt0],dp[i-1][j][k]); \]

当s[i] == ‘1’ 时 :

\[dp[i][j][k+cnt1]=min(dp[i][j][k+cnt1],dp[i-1][j][k]+1);//改为0 \]

\[ dp[i][j+1][k-cnt0]=min(dp[i][j+1][k-cnt0],dp[i-1][j][k]);//不改 \]

当s[i] == '0' 时 :

\[dp[i][j][k+cnt1]=min(dp[i][j][k+cnt1],dp[i-1][j][k]);//不改 \]

\[dp[i][j+1][k-cnt0]=min(dp[i][j+1][k-cnt0],dp[i-1][j][k]+1);//改为1 \]

由于是交换01数,所以最终整个串的1的数量已知,操作数为翻转01数的二分之一。最终只需要输出dp[n][cnt1][3000] / 2 即可。

D代码:

#include<bits/stdc++.h>
using namespace std;
int dp[105][105][6000];
void solve() 
{
  memset(dp,0x3f,sizeof dp);
  dp[0][0][3000]=0;
  string s;
  cin>>s;
  s=" "+s;
  int l=s.size();
  int ans=0;
  for(int i=1;i<l;i++){
    if(s[i]=='1'){
      ans++;//记录1的个数
    }
  }
  for(int i=1;i<l;i++){
    for(int j=0;j<i;j++){
      int cnt1=j;
      int cnt0=i-1-j;
      for(int k=500;k<=5500;k++){
        if(s[i]=='1'){
          dp[i][j+1][k-cnt0]=min(dp[i][j+1][k-cnt0],dp[i-1][j][k]);//不改
          dp[i][j][k+cnt1]=min(dp[i][j][k+cnt1],dp[i-1][j][k]+1);//改为0
          
        }
        else{
          dp[i][j+1][k-cnt0]=min(dp[i][j+1][k-cnt0],dp[i-1][j][k]+1);//改为1
          dp[i][j][k+cnt1]=min(dp[i][j][k+cnt1],dp[i-1][j][k]);//不改
         
        }
      }

    }
  }
  cout<<dp[l-1][ans][3000]/2<<endl;

}  
int main(){
  int t;
  // cin>>t;
  t=1;
  
  while(t--){
    solve();
  }
  return 0;

}