牛客周赛Round 1:暑假第二天训练计划

本文介绍了三道编程竞赛题目,包括图像模拟、数组染色问题和01串的操作。解题策略涉及模拟、乘法原理和动态规划,展示了在算法设计和问题解决中的思路。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

今天浅浅vp一场7月2号的周赛

牛客竞赛_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ

A题:游游画U

纯模拟题:我的做法是把图像看成上下两部分第一部分是1-3*n行,第二部分是后面n行,每一行的星号和点的个数是n的某个倍数(这里我就不多说了让大家想想)

直接上代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	ll n;
	cin>>n;
	for(int i=1;i<=n*3;i++)
	{
		for(int j=1;j<=n;j++)
		{
			cout<<"*";
		}
		for(int k=1;k<=2*n;k++)
		{
			cout<<".";
		}
		for(int q=1;q<=n;q++)
		{
			cout<<"*";
		}
		cout<<endl;
	}
	
		for(int i=1;i<=n;i++)
	{
		for(int p=1;p<=i;p++)
		{
			cout<<'.';
		}
		for(int z=1;z<=n;z++)
		{
			cout<<"*";
		}
		ll q=n*4;
		q-=i;
		q-=i;
		q-=n;
		q-=n;
		for(int w=1;w<=q;w++)
		{
			cout<<".";
		}
		for(int c=1;c<=n;c++)
		{
			cout<<"*";
		}
	for(int m=1;m<=i;m++)
		{
			cout<<".";
		}
		cout<<endl;
	}

}

有一说一有点丑陋

B题:游游的数组染色

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

游游拿到了一个数组,其中一些数被染成红色,一些数被染成蓝色。
游游想知道,取两个不同颜色的数,且它们的数值相等,有多少种不同的取法?
我们定义,两种取法如果取的某个数在原数组的位置不同,则定义为不同的取法。

考察乘法原理

我们只需要每一次找到一种颜色a然后把它的数字k,(mp[0][k])记录一下然后枚举每一位的答案就是:另一种颜色下数字是k的个数mp[1][k]*mp[0][k];

最后求个和就行

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
ll a[N];
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	ll n;
	map<int,int>mp[2];
	cin>>n;
	for(int i=1;i<=n;i++)
	{
		cin>>a[i];
	}
	string s;
	cin>>s;
	s='?'+s;
	for(int i=1;i<=n;i++)
	{
		if(s[i]=='R')
		{
			mp[1][a[i]]++;
		}
		else
		{
			mp[0][a[i]]++;
		}
	}
	ll res=0;
	for(auto it:mp[0])
	{
		ll x=it.second;
		ll y=mp[1][it.first];
		res+=(x*y);
	}
	cout<<res<<endl;

}

C题:链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

游游拿到了一个01串(仅由字符'0'和字符'1'构成的字符串)。游游每次操作可以交换两个相邻的字符,例如,对于字符串"11001"而言,游游可以交换第二个字符和第三个字符变成"10101"。
游游希望最终字符串任意两个相邻的字符都不相同,她想知道最少需要多少操作次数?保证答案是有解的,即最终一定能形成任意两个相邻的字符都不相同的字符串。

我的思路是这样的因为相邻的数字不同又因为是01串所以答案无非就两种:

1.01010101....

2.10101010....

我们只需要先构造出这两个字符串然后再想一下移动后的花费就行

因为花费要最小所以我们尽量只动一次

所以我的想法就是先编号是1的是由原编号是1的移动过来的

可以简单证明一下为啥:

可以看一下上面的图假设now1是pre2变过来的,原来pre1和pre2的0只是互换了,两个位置还是0,但是pre1-now1的路程明显是pre2-now1短的,后面第2个0的位置不变说明后面的答案是没影响的,但是第一个0的代价多了一,所以按顺序移动的代价是最小的

所以我们只需要处理处理0101010..和10101010...的代价后直接取min就行

我最后代码还加了一个小小的剪枝通过看原序列0的个数是不是两种构造都成立,因为比如010和101它们0的个数是不一样的,如果原序列是110的话只能构造101了

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
ll a[N];
ll pre[N],now1[N],now2[N];
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	string s;
	cin>>s;
	//11100
	//10101
	string tmp1,tmp2;
	for(int i=0;i<s.size();i++)
	{
		if(i%2==0)tmp1+='1',tmp2+='0';
		else tmp1+='0',tmp2+='1';
	}
	ll cnt=0;
	for(int i=0;i<s.size();i++)
	{
		if(s[i]=='0')
		{
			pre[++cnt]=i+1;
		}
	}
	ll ans=0;
	ll qnc=0;
	for(int i=0;i<tmp1.size();i++)
	{
		if(tmp1[i]=='0')
		{
			now1[++ans]=i+1;
		}
		if(tmp2[i]=='0')
		{
			now2[++qnc]=i+1;
		}
	}
	ll sum1=0;
	ll sum2=0;
	for(int i=0;i<tmp1.size();i++)
	{
		sum1+=abs(pre[i]-now1[i]); 
	}
	for(int i=0;i<tmp2.size();i++)
	{
		sum2+=abs(pre[i]-now2[i]);
	}
	
	if(cnt==ans&&cnt!=qnc)
	{
		cout<<sum1<<endl;
	}
	else if(cnt!=ans&&cnt==qnc)
	{
		cout<<sum2<<endl;
	}
	else if(cnt==ans&&cnt==qnc)
	{
		cout<<min(sum1,sum2)<<endl;
	}

}

D题

链接:登录—专业IT笔试面试备考平台_牛客网
来源:牛客网

游游拿到了一个数字串,她想取一个该数字串的子序列(子序列在原串中可以不连续),使得该子序列是9的倍数。子序列可以包含前导零。
游游想知道,一共能取多少个合法的子序列?答案请对 109+7取模。
序列题一般都可以dp解决的

这里有个知识,序列如果是9的倍数的话那序列各个位置的数字之和的余数是0

我们可以设置dp[i][j]表示前i个数中余数是j的组合的个数

那怎么转移呢?

我们首先要预处理一下

for(int i=0;i<n;i++)
	{
		dp[i+1][(s[i]-'0')%9]++;
		//先把每一个位置上的数的余数统计出来 
	}

我们知道单个数也可以构成序列的,所以第i个数的本身可能也是9的倍数这算一种

dp[i-1][j]是不算第i个字符的

dp[i-1][(9+j-(s[i]-'0'))%9]是算第i个字符的

转移的话就是这三个之和

因为我们是从小到大转移的所以在第i个数还没被更新的时候它原来的答案就是第一种(第一种我们已经先预处理过了)

(9+j-(s[i]-'0'))%9这个大家可能会不理解我这里解释一下:

如果算上第i个字符答案子序列和的余数是j的倍数,必须前i-1个字符的余数是j-(s[i]-'0')这样加上(s[i]-'0')时候余数才会是j

置于加9是因为可能会出现负数所以+9后外面再%9(这个技巧很常见)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2e5+10;
const int mod=1e9+7;
int dp[N][15];
int main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	string s;
	cin>>s; 
	int n=s.size();
	for(int i=0;i<n;i++)
	{
		dp[i+1][(s[i]-'0')%9]++;
		//先把每一个位置上的数的余数统计出来 
	}
	for(int i=1;i<=n;i++)
	{
		for(int j=0;j<=8;j++)
		{
			dp[i][j] = (dp[i][j]+dp[i-1][j]+dp[i-1][(9+j-(s[i-1]-'0'))%9])%mod;
		}
	}
	cout<<dp[n][0]%mod<<endl;
	
	

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值