2025 SMU WINTER 4th

前言

内含蓝桥杯,天梯赛补题

蓝桥杯训练1

P8598错误票据

赛时对50分,赛后检查发现是因为我交的代码当出现"0 1 1 1 "这种样例时,断点的判断就不对(赛时交的代码是通过a[i]-1!=a[i-1]来判断的。这样在连续的数字的时候不行)
补题时用了set来去重

点击查看代码
void solve() {
	ll n;cin>>n;
	ll x;
	ll duan=0,chong=0;
	vector<ll>a;
	set<ll>st;
	ll minn=INT_MAX;
	while(cin>>x)
	{
		a.push_back(x);
		st.insert(x);
		minn=min(minn,x);
	}
	sort(a.begin(),a.end());
	
	for(ll i=1;i<(ll)a.size();++i)
	{
		if(a[i]==a[i-1])
		{
			chong=a[i];
			break;
		}
	}
	
	ll cnt=minn;
    for(auto t:st)
	{

		if(t!=cnt)
		{
			duan=t-1;
			break;
		}
		cnt++;
		
	}
	cout<<duan<<" "<<chong;
}

B4022蓝色的网易云

题意:给每首歌编一个播放顺序,使得相邻歌曲播放的题材不同
最开始我用了sort,但后面意识到,最后输出结果是要和输入的数据一比一对照的。
所以后面用vector开二维数组,记录编号

点击查看代码
void solve() {
    ll n,m;cin>>n>>m;
    vector<ll>c(n);
	vector<vector<ll>> num(m+10); //记录编号
	for(ll i=0;i<n;++i)
	{
		cin>>c[i];
		num[c[i]].push_back(i+1);
	}
	
	vector<ll>ans;
	while(ans.size()<n)
	{
		for(ll i=1;i<=m;++i)
		{
			if(!num[i].empty())
			{
				ans.push_back(num[i].back());
				num[i].pop_back();
			}
		}
	}
	
	for(auto t:ans)
	{
		cout<<t<<'\n';
	}
}

P8605网络寻路

图论忘得一干二净,,
详见代码

点击查看代码

void solve() {
	ll n,m;
	ll ans=0;
	cin>>n>>m;
	ll d[n+10]={0};
	ll u[n+10]={0},v[n+10]={0};
	for(ll i=0;i<m;++i)
	{
		cin>>u[i]>>v[i];   //各个节点
		d[u[i]]++;         //节点的入度
		d[v[i]]++;
	}
	for(ll i=0;i<m;++i)
	{
		if(d[u[i]]>1&&d[v[i]]>1)
		{
			ans+=(d[u[i]]-1)*(d[v[i]]-1)*2;  //计算该边作为中间边的个数*2
		}
	}
	cout<<ans;
}

P8845小卡和质数

纯想暴力解去了,一点也没观察规律。
因为两个质数,只有为1和2时,才会异或成1

P8739重复字符串

题意:现有给定的一个字符串,最少需要多少次,可以时字符串变成一个k段且一样的字符串
贪心,我们先将原字符串分成k段(不能整除的就输出-1),判断每个段的每个位置出现频率最高的字母,再把每段每个位置的字母和频率最高的字母的不同给统计起来。

点击查看代码
void solve() {
    ll k;cin>>k;
	string s;cin>>s;
	ll n=s.size();
	ll ans=0;
	s=" "+s;
	if(n%k)
	{
		cout<<"-1\n";
		return ;
	}
	ll temp=n/k;
	for(ll i=1;i<=temp;++i)
	{
		ll a[30]={0};
		ll tot=0;
		for(ll j=i;j<=n;j+=temp)
		{
			a[s[j]-'a']++;
			tot=max(tot,a[s[j]-'a']);
		}
		ans+=(k-tot);
	}
	cout<<ans<<'\n';
}

P10426宝石组合

image
(从洛谷上看来的题解)
由容斥原理我们可以将原式化简
s=j=gcd(ha,hb,hc);
image
(让人长脑子的思路啊)

赛时准备暴力解得三十分来着,但是代码里面直接用的是lcm〒▽〒,写得过程中还想着要么手写遍lcm,要么转换成__gcd(),但是给忘了。。导致编译失败,赛后才发现
反思:
1.想到要做补什么就去补,别给忘了
2.蓝桥杯这种赛时看不到结果的,多留心下编译是否成功(可能编译器版本不对)

点击查看代码
const ll N = 1e5 + 10;
const ll mod=1e9+7;

ll cnt[N];
ll h[N];

void solve() {
    ll n;cin>>n;
	for(ll i=1;i<=n;++i)
	{
		cin>>h[i];
	}
	for(ll i=1;i<=n;++i)
	{
		for(ll j=1;j*j<=h[i];++j)
		{
			if(h[i]%j==0)
			{
				cnt[j]++;
				if(j*j!=h[i])
				{
					cnt[h[i]/j]++;
				}
			}
		}
	}
	ll numgcd=0;
	for(ll i=N-10;i>=1;--i)
	{
		if(cnt[i]>=3)
		{
			numgcd=i;
			break;
		}
	}
	sort(h+1,h+1+n);
	ll tot=0;
	for(ll i=1;i<=n;++i)
	{
		if(h[i]%numgcd==0)
		{
			cout<<h[i]<<" ";
			tot++;
		}
		if(tot==3)
		{
			return ;
		}
		
	}
	
}

P10429拔河

题意:在给出的一串序列中找到将其分成两块的分界,使得两边差值最小
看题解有说用双指针做的
感觉自己双指针还是用的不够,学了之后还是得多写写(上个学期刚开始学感觉头大的stl和前缀和现在也觉得用的不错了)

e.p:下面代码中有三个while
第一个是同时扩展
当扩展到一个指针超过边界时,就会break第一个while
这时候就需要第二三个while继续来找到最小答案的情况

点击查看代码
const ll N = 1e3+ 10;

ll a[N];
ll ans=INT_MAX;

void solve() {
	ll n;cin>>n;
	for(ll i=1;i<=n;++i)
	{
		cin>>a[i];
	}
	for(ll i=1;i<=n;++i)
	{
		for(ll j=i+1;j<=n;++j)
		{
			ll l=i-1,r=j+1;
			ll suml=a[i],sumr=a[j];
			ans=min(ans,abs(suml-sumr));
			while(l>=1&&r<=n)
			{
				if(suml>sumr) sumr+=a[r++];
				else suml+=a[l--];
				ans=min(ans,abs(suml-sumr));
			}
			while(l>=1)
			{
				suml+=a[l--];
				ans=min(ans,abs(suml-sumr));
				
			}
			while(r<=n)
			{
				sumr+=a[r++];
				ans=min(ans,abs(suml-sumr));
			}
		}
	}
	cout<<ans<<'\n';
}

反思

第一次蓝桥杯训练,说实话感觉感觉训练得并不是很好,感觉有些题做起来有点生疏(思路卡卡的),对我来说不是很好上手〒▽〒
还是得多练习搜索的〒▽〒

天梯训1

L1-6 剪切粘贴

题意:要编一个程序实现剪切粘贴的功能(剪切和粘贴的位置都会给出)
我最开始没注意到要粘贴的位置都是连在一起的,就导致总是达不到样例

解决要粘贴的位置是连在一起的也简单:直接先定义一个字符串将其连在一起,在原字符串中查找这个字符串

点击查看代码
void solve() {
	string s;cin>>s;
	s=" "+s;
	ll n;cin>>n;
	while(n--)
	{
		ll a,b;cin>>a>>b;
		string s0=s.substr(a,b-a+1);  //用substr函数
		s.erase(a,b-a+1);             //erase函数
		
		string c,d;cin>>c>>d;
		
		string s1=c+d;             //合在一起去查找
	    
		auto poss0=s.find(s1);
		
		if(poss0==string::npos)
		{
			s+=s0;
		}else{
			s.insert(poss0+c.size(),s0);
		}

	}
	s.erase(s.begin());
	cout<<s<<'\n';
}

L1-7 分寝室

这道题交第一发卡了两个测试点没过,后面觉着应该是题目条件没满足
1.不允许单人睡一间寝室(我还是特判了下)

for(ll i=1;i<=n&&i<n0&&(n-i)<n1;++i)
注意:这题我是使用x+y=1使得y=x-1和x,所以在循环的过程中要注意循环条件的满足

L2-1 堆宝塔

image

样例,答案是4 5
11
10 8 9 5 12 11 4 3 1 9 15

思路:按照题目描述模拟即可,但是要注意最开始是如果a为空或者要插入的元素比a数组里最后的元素小才插入a数组(第一次写就是没判a为空,导致数据一直进不去)
最后也要注意判,如果a数组还有cnt++,b数组同理

L2-2 天梯赛的赛时安排

这个困住我的是想不明白怎么去处理分够c之后剩下的人,看了别人写的之后,发现用优先队列写的,就很好处理了剩下的人的问题。(用优先队列而不是队列,我觉得是贪心思想的体现(先把剩的人多的队伍排好(题目有要求同一队尽可能同一考场))
思路:一行一行读入字符串和数组,然后直接计算输出,留下res待后续算剩下的人要多少房间。循环,当队列非空的时候,取顶部元素(即优先队列里面的最大值),for循环ans数组(也就是每个队伍剩的人),如果该元素小于等于c减去ans[i],那么就纳入ans[i](感觉这个地方的处理也很巧妙,直接纳入)。如果循环下来还是没有的话,就单开一间

点击查看代码
void solve() {
    ll n,c;cin>>n>>c;
	ll res=0;
	priority_queue<ll>q;
	for(ll i=1;i<=n;++i)
	{
		string s;cin>>s;
		ll x;cin>>x;
		cout<<s<<" "<<(x+c-1)/c<<endl;  //(x+c-1)能防止类似于c为30时,数据为28的情况
		res+=x/c;
		if(x%c)
		{
			q.push(x%c);
		}
	}
	vector<ll>ans;
	while(!q.empty())
	{
		ll temp=q.top();
		q.pop();
		bool f=false;
		for(ll i=0;i<(ll)ans.size();++i)
		{
			if(temp<=c-ans[i])
			{
				f=true;
				ans[i]+=temp;
				break;
			}
		}
		if(!f)
		{
			ans.push_back(temp);
			res++;
		}
	}
	cout<<res<<endl;
}

L2-4 寻宝图

image

样例,答案是 7 2
10 11
01000000151
11000000111
00110000811
00110100010
00000000000
00000111000
00114111000
00110010000
00019000010
00120000001

用的bfs做

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define endl '\n'
const ll N = 1e5 + 10;

ll dx[]={-1,1,0,0};
ll dy[]={0,0,-1,1};

vector<string>g;
map<pair<ll,ll>,bool>vis;
ll n,m;

bool check(ll a,ll b)
{
	if((a>=0&&a<n)&&(b>=0&&b<m)&&(g[a][b]-'0')&&(!vis[{a,b}]))
	{
		return true;
	}
	return false;
}

ll bfs(ll a,ll b)
{
	queue<pair<ll,ll>>q;
	q.push({a,b});
	vis[{a,b}]=true;
	bool have=false;
	while(q.size())
	{
		ll x=q.front().first;
		ll y=q.front().second;
		q.pop();
		if(g[x][y]>='2'&&g[x][y]<='9')
		{
			have=true;
		}
		for(ll k=0;k<4;++k)
		{
			ll u=x+dx[k];
			ll v=y+dy[k];
			if(check(u,v))
			{
		//		cout<<u<<" "<<v;
				vis[{u,v}]=true;
				q.push({u,v});
			}
		}
		
	}
	if(have) return 1;
	else return 0;
}

void solve()
{
    cin>>n>>m;
	getchar();
	for(ll i=0;i<n;++i)
	{
		string s;
		getline(cin,s);
		g.push_back(s);
		
	}
	ll cnt=0,val=0;
	for(ll i=0;i<n;++i)
	{
		for(ll j=0;j<m;++j)
		{
			if(g[i][j]=='0'||vis[{i,j}])
			{
				continue;
			}
			val+=bfs(i,j);
		    cnt++;
	//		cout<<i<<" "<<j<<'\n';
		}
	}
	cout<<cnt<<" "<<val;
}

int main()
{
//	ios::sync_with_stdio(false),cin.tie(nullptr),cout.tie(nullptr);
	int t=1;
//	cin>>t;
	while(t--)
	{
		solve();
	}
	return 0;
}

这段代码我贴的完整代码,是因为我卡了一下午,而罪魁祸首是我同时用了关闭流同步和getchar()
image
image

蓝桥杯训练2

P8692 数正方形

image

点击查看代码
const ll mod=1e9+7;

void solve() {
	ll n;cin>>n;
	n-=1;
	ll ans=0;
	for(ll i=1;i<=n;++i)
	{
		ll temp=(n-i+1);
		ans=(ans+(temp*temp*i))%mod;
	}
	cout<<ans<<'\n';
}

学到了这种思考方式,之前碰到这种题老是会想概率(组合)啥的)

总结

这周是寒假训练最后一周了,状态比上周好。但是还是有点不适应蓝桥杯赛制啊啊啊,做题总有思考不到的地方。
温故了bfs算法,之前都是用在类似于寻宝图这种题上,其他类型的题目接下来几天得趁热练练手。

posted @ 2025-02-17 23:30  YuanqLi  阅读(52)  评论(0)    收藏  举报