2022年中国高校计算机大赛-团队程序设计天梯赛(GPLT)上海理工大学校内选拔赛 题解

A题

在这里插入图片描述

思路:
分两种情况:
1.除了最大的数 其他的都是与最大的数的和最大
2.最大的数与第二大的数的和最大

 代码实现
 法一:利用pair的对应赋值关系实现
#include<bits/stdc++.h>
using namespace std;
const int M=1e5+10;
typedef pair<int,int> PII;
PII a[M];
ll res[N];
int main()
{
	ll n;
	cin>>n;
	for(ll i=1;i<=n;i++){
		cin>>a[i].first;
		a[i].second=i;
	}
	sort(a+1,a+1+n);
	for(ll i=1;i<=n;i++){
		
		int id=a[i].second;
		if(i!=n){
			res[id]=a[i].first+a[n].first;
			
		}
		else {
			res[id]=a[i].first+a[i-1].first;
		}
		
	}
	for(int i=1;i<=n;i++){
		cout<<res[i]<<" ";
	}
} 
法二:利用数组赋值实现
#include <bits/stdc++.h>
using namespace std;
int main()
{long long n,i,j,max,k,sum;
 cin>>n;
 int a[n],b[n];
 for(i=0;i<n;i++)
   cin>>a[i];
 for(i=0;i<n;i++)
   b[i]=a[i];
 sort(a,a+n);
 for(i=0;i<n;i++)
  if(b[i]!=a[n-1])
   cout<<b[i]+a[n-1]<<" ";
  else
   cout<<a[n-2]+a[n-1]<<" ";
}

总结

一题多解 多重标记的多重考虑 数组/pair 标记 维护序列的顺序
再逐一输出

B题

在这里插入图片描述

快速幂的典型板子题 注意ll 不要溢出了
小心细节 小心驶得万年船

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll qmi(ll a,ll b,ll p){
    ll res=1;
    while(b){
    if(b&1)res=res*a%p;
    a=a*a%p;
    b>>=1;
    }
    return res;
}
int main(){
    ll n;
    cin>>n;
    ll m=qmi(n,n,n+2);
    printf("%lld\n",m);
    return 0;
}

C题

在这里插入图片描述

思路:
先接收一个字符串 再把小字符串和数字分开
用map装住 再询问

注意!!!!
由于字符串和整型int的区别 电脑无法区分两者 所以不能多次快速接收
一定要手动分开!!!

有两种区分方法:
法一:字符串函数

知识点1:! ! stringstream
cin/s stringstream ss(str); //定义一个stringstream类型的对象并用str进行初始化
while(ss >> x) //可以理解为cin >> , cin也是以空格或者回车作为结束的标准
//实际上cin应该也是类似于这种输入,都是通过流来输入的。
在这里插入图片描述

代码实现:
#include<bits/stdc++.h>
using namespace std;
map<string,int> mp;

int main(){
	ios::sync_with_stdio(false);
	cin.tie(0); 
	string ss;//先把全部的对看成一个字符串
	getline(cin,ss);//再进行分割
	stringstream sin;
	sin<<ss;//将ss转变成输入流 
	int k;
	string a;
	while(sin>>a>>k){
		mp[a]+=k;//有可能有重复的a 
	} 
	int t;
	cin>>t;
	while(t--){
        string s;
		cin>>s;
		cout<<mp[s]<<endl;
	}
		
}

总结/易错点:
cin或定义的stringstream只能根据空格或换行分割
如用其他字符进行分割的 必须先进行转换(作为字符数组/字符串 接收进来进行循环转换)

法二:自行分割后输入
#include<bits/stdc++.h>
using namespace std;
map<string,int> mp;

int main(){
	char c;
	int n;
	string s;
	cin>>s>>n;//正常接收字符串 遇到整型就停止了
	scanf("%c",&c);//接收了个空格
	while(1){
		if(c==10)break;//  \n的ascii码为10
		cin>>s>>n;
		scanf("%c",&c);
		if(mp[s])mp[s]+=n;
		else mp[s]=n; 
	} 
	
	int t;
	cin>>t;
	while(t--){
        string s;
		cin>>s;
		cout<<mp[s]<<endl;
	}
		
}

总结:
最笨的方法
一个个输入 将输入的数据分为 字符串 整型 字符
再不断循环(至少先接收一次)

D题

在这里插入图片描述

典型的数学题
思路:找规律 二进制

E题

在这里插入图片描述

思路:

#include<bits/stdc++.h>
using namespace std;
typedef pair<int,int> PII;
const int N=310;
int n,m,k,a,b,c,d;
int g[N][N];
int dx[4]={-2,-2,-1,1,-1,1,2,2},dy[4]={-1,1,-2,-2,2,2,-1,1};
int dist[N][N],dis[N][N];//一个国际 一个中国 
void bfs(){
	queue<PII>heap;
	heap.push({a1,b});//把起点坐标加入队列
	
	while(heap.size())//当队列不为空
	{
		auto tt=heap.front();
		int x=tt.first,y=tt.second;
		if(x==c&&y==d)return;//到达终点
		heap.pop();//把队头删掉
		
		for(int i=0;i<8;i++)//八个方向不断遍历
		{
			int l=x+dx[i],r=y+dy[i];
			if(l<1||l>n||r<1||r>m)continue;
			int k=i/2;
			if(k==0&&(a[x-1][y] + a[l][r])) //再加入4个特判
			
			
			
			if(dist[l][r]>dist[x][y]+1)
			{
				dist[l][r]=dist[x][y]+1;
				heap.push({l,r});//l,r 加入队列 
			}	 
		 } 
	 } 
}
void bfs1(){
	queue<PII>heap;
	heap.push({a1,b});//把起点坐标加入队列
	
	while(heap.size())//当队列不为空
	{
		auto tt=heap.front();
		int x=tt.first,y=tt.second;
		if(x==c&&y==d)return;//到达终点
		heap.pop();//把队头删掉
		
		for(int i=0;i<8;i++)//八个方向不断遍历
		{
			int l=x+dx[i],r=y+dy[i];
			if(l<1||l>n||r<1||r>m)continue;
			
			if(dist[l][r]>dist[x][y]+1){
				dist[l][r]=dist[x][y]+1;
				heap.push({l,r});//l,r 加入队列 
		 } 
	 } 
}
int main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	int t;
	while(t--){
		cin>>n>>m>>k>>a1>>b>>c>>d;
		for(int i=0;i<k;i++){
			int x,y;
			cin>>x>>y;
			a[x][y]=1;//记录此地方不能走 
		}
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++)
				dist[i][j]=dis[i][j]=1e9;}
		dist[a1][b]=dis[a1][b]=0;
		if(a[a1][b]+a[c][d])//起点和终点都没有棋子 
		{
			printf("-1 -1\n");
			for(int i=1;i<=n;i++)
				for(int j=1;j<=n;j++)
					a[i][j]=0; 
			continue;
	}
	bfs();
	bfs1();
	if(dis[c][d]<1e9)printf("%d",dis[c][d]);
	if(dist[c][d]<1e9)printf("%d",dist[c][d]); 
	
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			a[i][j]=0;
		}	
	}

G题

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e6+10;
int a[N];//用于存储前i天有多少天是晴天 
char ss[N];
int main(){
	ll n,x,y;
	cin>>n>>x>>y;
	scanf("%s",ss+1);//表示从下标为1的数组开始记录
	for(int i=1;i<=n;i++){
		a[i]=a[i-1];
		if(ss[i]=='0')a[i]++;
	} 
	
	
	ll res=0;
	for(int i=1;i<=n;i++){
		int l=i,r=n;
		while(l<r){
			ll mid=l+r>>1;
			ll rest=a[mid]-a[i-1];
			if(rest>=x&&(mid-i+1)-rest>=y)r=mid;
			else l=mid+1;
			
		}
		ll rest=a[r]-a[i-1];
		ll p=(r-i+1)=rest;
		if(rest>=x&&p>=y)
		res+=(n-r+1);
	} 
	if(x+y==0)res+=1;
	cout<<res;
}

H题

在这里插入图片描述
在这里插入图片描述

int dp[N][N];//最简单的01背包问题 
int h[N];
int main()
{
	int n,k;
	cin>>n>>k;
	memset(dp,0x3f,sizeof dp);
	dp[0][0]=0;
	for(int i=1;i<=n;i++){
		cin>>h[i];
		dp[i][0]=0;
		// dp[i][j]表示的是从前i个里面选高度为j的堆数量
		}
	sort(h+1,h+1+n);
	for(int i=1;i<=n;i++){
		for(int j=1;j<=3000;j++)//测试点的最大高为3000 
		dp[i][j] = dp[i-1][j];
		if(j>=h[i]){
			dp[i][j]=min(dp[i-1][j],dp[i-1][j-h[i]]+1);
		}
	}
	//计算过程结束
	//接下来判断结果 特例 
	if(dp[n][k]==0x3f3f3f3f){
		cout<<"-1";
		return 0;
	}
	cout<<dp[n][k]<<endl;
	
	vector<int>res;
	for(int i=n;i>=1;i--)//h已经按照升序排序  则需要从n到1遍历
	//此过程为 倒着找硬币堆 并拉入stl 
	{
		if(k>=h[i]&&dp[i-1][k-h[i]]+1==dp[n][k]){
			res.push_back(h[i]);
			k-=h[i];//已知到答案 一步步倒推每个元素 
		}
	}
	sort(res.begin(),res.end());//多解则按字典序排列 
	for(int i=0;i<res.size();i++)//遍历每个答案 
	{
		cout<<res[i]<<" ";
	} 
	 
}

K题

在这里插入图片描述

思路:利用数学公式
学会巧妙的用o(1)的算法
先分区间 在进行公式套用
在这里插入图片描述

  #include"bits/stdc++.h"
  using namespace std;
  typedef long long ll;
  const int mod=998244353;
  const int inv2=(998244353+1)/2;
  ll n;
  int main(){
  	cin>>n;
  	ll ans=(n % mod)*(n % mod) %mod;
  	for(ll i=1,r;i<=n;i=r+1){
  		r=n/(n/i);//相同商的右端点
		ans-=(n/i)%mod*((r+i)%mod)%mod*((r+1-i)%mod)%mod*inv2%mod; 
  		ans=(ans%mod+mod)%mod;
	  }
	  cout<<ans<<'\n';
	  return 0;
  } 

总结:
本次题解就到这里啦!!我们下一次赛后见~
每次回顾 写题解是我最快乐的时光嘿嘿~~

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值