Codeforces Round 933 (Div. 3)

本文分析了四道Codeforces竞赛题目,涉及暴力枚举、贪心算法(包括左右操作数组元素)、动态规划(如球游戏问题)以及字符串匹配,展示了如何利用这些方法优化时间复杂度并求解问题。
摘要由CSDN通过智能技术生成

A:Rudolf and the Ticket

题面:

Problem - A - Codeforces

分析:

暴力枚举

时间复杂度O(n^2),当然可以排序二分优化到O(nlog n)

代码:

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
const int N=1e2+10,M=1e9+7;
int n,m,k;
int b[N],c[N];
void solve()
{
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++){
		cin>>b[i];
	}
	for(int i=1;i<=m;i++){
		cin>>c[i];
	}
	int ans=0;
	for(int i=1;i<=n;i++){
		for(int j=1;j<=m;j++){
			if(b[i]+c[j]<=k){
				ans++;
			}
		}
	}
	cout<<ans<<'\n';
}
int main()
{
	std::ios::sync_with_stdio(0);
	std::cin.tie(0);
	int T=1;
	cin>>T;
	while(T--)
	{
		solve();	
	}	
	return 0;
} 

B:Rudolf and 121

题面:

Problem - B - Codeforces

分析:

贪心

可以发现,最左侧和最右侧的非零元素只能分别通过操作右侧和左侧元素将其变为0

所以可以从两侧开始模拟操作,如果最后能将数组变成全0则YES,否则NO

当然,也可以单纯地从左往右或从右往左模拟

被操作的位置会变成下一个边界

代码:

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
const int N=2e5+10,M=1e9+7;
int n;
int a[N];
void solve()
{
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	int l=2,r=n-1;
	while(l<=r)
	{
		if(a[l-1]>0){//注意不要操作小于0的数,等于0无需操作
			a[l]-=2*a[l-1];
			a[l+1]-=a[l-1];
			a[l-1]=0;
		}
		if(a[r+1]>0){
			a[r]-=2*a[r+1];
			a[r-1]-=a[r+1];
			a[r+1]=0;
		}
		l++;
		r--;
	}
	int flag=1;
	for(int i=1;i<=n;i++){
		if(a[i]!=0){
			flag=0;
			break;
		}
	}
	if(flag){
		cout<<"YES"<<'\n';
	}else{
		cout<<"NO"<<'\n';
	}
}
int main()
{
	std::ios::sync_with_stdio(0);
	std::cin.tie(0);
	int T=1;
	cin>>T;
	while(T--)
	{
		solve();	
	}	
	return 0;
} 

C:Rudolf and the Ugly String

题面:

Problem - C - Codeforces

分析:

字符串匹配,贪心

对于单独的map或者pie,只要删除其中任意一个字符即可

注意到map和pie尾部和首部分别都有一个p,那么对于mapie只要删除p即可,操作一次

模拟匹配删除操作

代码:

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
typedef pair<ll,ll> pll;
const int N=1e6+10,M=1e9+7;
int n;
string s,t1,t2;
void solve()
{
	cin>>n>>s;
	int l=0,ans=0;
	while(l<=n-3)
	{
		int flag=0;
		if(s[l]=='m'&&s[l+1]=='a'&&s[l+2]=='p'){
			ans++;
			l+=2;
			flag=1;
		}
		if(s[l]=='p'&&s[l+1]=='i'&&s[l+2]=='e'){
			if(flag==0){
				ans++;
			}
			l+=2;
		}
		l++;
	}
	cout<<ans<<'\n';
}
int main()
{
	std::ios::sync_with_stdio(0);
	std::cin.tie(0);
	int T=1;
	cin>>T;
	while(T--)
	{
		solve();	
	}	
	return 0;
} 

D:Rudolf and the Ball Game

题面:

Problem - D - Codeforces

分析:

动态规划

有一道与这题近乎一样的题:

D-我不是大富翁_牛客小白月赛88 (nowcoder.com)

题解:

牛客小白月赛88-CSDN博客

在以上做法的基础上修改:

因为初始位置不固定,边界条件修改成dp[0][x-1]=1

状态转移过程由c[i]来决定

输出答案时遍历dp[m][j]存储输出

代码(1):

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
const int N=1e3+10,M=998244353;
int n,m,x;
int r[N];
char c[N];
int dp[N][N];
void solve()
{
	cin>>n>>m>>x;
	for(int i=1;i<=m;i++){
		cin>>r[i]>>c[i];
	}
	for(int i=0;i<=m;i++){
		for(int j=0;j<=n;j++){
			dp[i][j]=0;
		}
	} 
	dp[0][x-1]=1;
	for(int i=1;i<=m;i++){
		for(int j=0;j<=n-1;j++){
			int last1=((j-r[i])%n+n)%n,last2=(j+r[i])%n;
			if(c[i]=='0'){
				dp[i][j]=max(dp[i][j],dp[i-1][last1]);
			}else if(c[i]=='1'){
				dp[i][j]=max(dp[i][j],dp[i-1][last2]);
			}else{
				dp[i][j]=max(dp[i][j],max(dp[i-1][last1],dp[i-1][last2]));
			}
		}
	}
	vector<int> v;
	for(int i=0;i<=n-1;i++){
		if(dp[m][i]==1){
			v.push_back(i+1); 
		}
	}
	cout<<v.size()<<'\n';
	for(auto &t:v){
		cout<<t<<" ";
	}
	cout<<'\n';
}
int main()
{
	std::ios::sync_with_stdio(0);
	std::cin.tie(0);
	int T=1;
	cin>>T;
	while(T--)
	{
		solve();	
	}	
	return 0;
} 

除了动态规划,还可以用set来限制复杂度,让暴力枚举的位置数始终在n以内(因为set去重,不同的位置最多只有n个)

代码(2):

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
const int N=1e3+10,M=998244353;
int n,m,x;
int r[N];
char c[N]; 
void solve()
{
	cin>>n>>m>>x;
	for(int i=1;i<=m;i++){
		cin>>r[i]>>c[i];
	}
	set<int> st[2];//开两个set轮流存
	int flag=0;
	st[flag].insert(x-1);
	for(int i=1;i<=m;i++){
		while(st[flag].size()){
			int t=*st[flag].begin();
			if(c[i]!='0'){
				st[flag^1].insert(((t-r[i])%n+n)%n);	
			}
			if(c[i]!='1'){
				st[flag^1].insert((t+r[i])%n);
			}
			st[flag].erase(t);
		}
		flag^=1;//换个set
	}
	cout<<st[flag].size()<<'\n';
	for(auto &t:st[flag]){
		cout<<t+1<<" "; 
	}
	cout<<'\n';
}
int main()
{
	std::ios::sync_with_stdio(0);
	std::cin.tie(0);
	int T=1;
	cin>>T;
	while(T--)
	{
		solve();	
	}	
	return 0;
} 

E:Rudolf and k Bridges

题面:

Problem - E - Codeforces

F:Rudolf and Imbalance

题面:

Problem - F - Codeforces

分析:

贪心、二分

只能在一个位置插入d[i]+f[j],让a[i]-a[i-1](i>1)(即间隔gap)的最大值最小

那么只能在最大的gap处插入,其他地方插入答案不变,并且d[i]+f[j]越靠近(a[i]-a[i-1])/2越好,如果将最大的gap分成的两个部分小于第二大的gap,答案为第二大的gap,否则答案为所有的分成的两个部分最大值的最小值

直接枚举d[i]和f[j]时间复杂度O(mk),可以先对f排序,再枚举d[i],二分合适的f[j]((a[i]-a[i-1])/2-d[i]),更新答案,时间复杂度O(mlog k)

代码:

#include<bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
typedef long long ll;
const int N=2e5+10,M=1e9+7;
ll n,m,k;
ll a[N],d[N],f[N];
void solve()
{
	cin>>n>>m>>k;
	for(int i=1;i<=n;i++){//都乘以2可以规避取整的问题
		cin>>a[i];
		a[i]*=2; 
	}
	for(int i=1;i<=m;i++){
		cin>>d[i];
		d[i]*=2;
	}
	for(int i=1;i<=k;i++){
		cin>>f[i];
		f[i]*=2;
	}
	ll maxn1=-1,maxn2=-1,l=0,r=0;
	for(int i=1;i<=n-1;i++){//找第一和第二大的gap
		ll t=a[i+1]-a[i];
		if(t>maxn1){
			maxn2=maxn1;
			maxn1=t;
			l=i;
			r=i+1;
		}else if(t>maxn2){
			maxn2=t;
		}
	}
	ll aver=(a[l]+a[r])/2;
	sort(f+1,f+k+1);
	ll ans=maxn1;
	for(int i=1;i<=m;i++){
		ll t=lower_bound(f+1,f+k+1,aver-d[i]+1)-f;//二分,+1枚举右侧会比较好处理
		if(t>k||(t>1&&abs((aver-d[i])-f[t-1])<abs((aver-d[i])-f[t]))){//修正t
			t--;
		}else if(t<1){
			t++;
		}
		if(d[i]+f[t]>=a[l]&&d[i]+f[t]<=a[r]){//能进行分隔
			ll res=max(a[r]-f[t]-d[i],d[i]+f[t]-a[l]);
			ans=min(ans,res);	
		}
	}
	ans=max(ans,maxn2);
	cout<<ans/2<<'\n';
}
int main()
{
	std::ios::sync_with_stdio(0);
	std::cin.tie(0);
	int T=1;
	cin>>T;
	while(T--)
	{
		solve();	
	}	
	return 0;
} 

G:Rudolf and Subway

题面:

Problem - G - Codeforces

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值