2025 寒假训练第一周

总体情况

最近过年琐碎的事情挺多,总觉得没有完全的投入到这个学习的过程中,有待改进。
牛客的题还有很多没补。

补题

考点:基础模拟

7-8 抓老鼠啊~亏了还是赚了?

其实这是一道简单的模拟,最核心需要处理的部分就是,当遇到‘C’以后,仍然保持两天的兴奋,其余的要求按照题意模拟就行。

首先,我们很容易的会想到使用bj1计数,来跳过T和X不派出老鼠的天数,当bj1重新减到0的时候,然后再重新派出老鼠,这一步是通过题意容易写出来的。

其次呢,如果C是两天的兴奋,那么如果这两天的兴奋中遇到了T或者X,那么C的优先级是高于T与X的,我们可以再开bj2来计数,这样当bj2大于0的时候,令遇到了T或X改变的bj1重新变为0,阅读代码可以很容易的理解优先关系。

void solve()
{
	string s;
	cin>>s;
	int ans=0;
	int len=s.size();
	int bj1=0,bj2=0;
	for(int i=0;i<len;i++)
	{
		if(s[i]=='$') break;
		if(bj2>0) {//通过bj2 来处理C后遇到的T或x 使其bj1重新为0
			bj1=0;
			bj2--;
		}
		
		if(!bj1){
			if(s[i]=='T'){
				cout<<"D";
				ans+=7;
				bj1=2;
			}
			if(s[i]=='X'){
				cout<<"U";
				bj1=1;
			}
			if(s[i]=='C'){
				ans-=3;
				cout<<"!";
				bj2=2;
			}
		}else {
			cout<<"-";
			bj1--;
		}
	
	}
	cout<<"\n"<<ans;
}

7-10 名人堂与代金券

同样是简单的模拟,利用结构体存储会方便一点,注意当两个人成绩相同的时候,其排名是一样的,但是当下一个成绩不同的时候,这个人的排名就会变为排完序后的第i个。

typedef struct{
	string s;
	int cj;
	int rk;
	
}stu;

stu a[100005];

void solve()
{
	int n,g,k; 
	cin>>n>>g>>k;
	for(int i=1;i<=n;i++) cin>>a[i].s>>a[i].cj;
	sort(a+1,a+n+1,[](stu a, stu b){
		if(a.cj!=b.cj)return a.cj>b.cj;
		return a.s<b.s;
	});
	
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		if(a[i].cj>=60&&a[i].cj<g) ans+=20;
		 if(a[i].cj>=g&&a[i].cj<=100) ans+=50;
		if(a[i].cj==a[i-1].cj) a[i].rk=a[i-1].rk;//这俩关键
		else a[i].rk=i;//1
	}
	
	cout<<ans<<"\n";
	for(int i=1;i<=n;i++)
	{
		if(a[i].rk<=k) cout<<a[i].rk<<" "<<a[i].s<<" "<<a[i].cj<<"\n";
	}
	
}

7-11 用扑克牌计算24点

这题是一个有意思的穷举法

\(a \quad op1 \quad b\quad op2\quad c\quad op3\quad d\)

这个等式可以放两个括号来放置优先级,那么就会得到如下五个

  • \(((a \quad op1 \quad b)\quad op2\quad c)\quad op3\quad d\)

  • \((a \quad op1 \quad (b\quad op2\quad c))\quad op3\quad d\)

  • \(a \quad op1 \quad(( b\quad op2\quad c)\quad op3\quad d)\)

  • \(a \quad op1 \quad( b\quad op2\quad (c\quad op3\quad d))\)

  • \((a \quad op1 \quad b)\quad op2\quad (c\quad op3\quad d)\)

这样我们首先就解决了一个运算顺序的问题,然后写五个函数来调用

然后我们就要解决,符号往哪里放,我们把加减乘除四个符号放到数组,三重循环暴力枚举每一种情况

最后,需要解决的就是这四个数放在什么位置,也就是一个全排列,可以直接调用next_permutation函数。

char op[4]={'+','-','*','/'};

double get( double x, double  y,char op)
{
	if(op=='+') return x+y;
	if(op=='-') return x-y;
	if(op=='*') return x*y;
	if(op=='/') return x/y;
}

double way1(double a,double b, double c, double d,char op1,char op2,char op3)
{
	double x=get(a,b,op1);
	double y=get(x,c,op2);
	double z=get(y,d,op3);
	

	return z;
}

double way2(double a,double b, double c, double d,char op1,char op2,char op3)
{
	double x=get(b,c,op2);
	double y=get(a,x,op1); //左右顺序别搞反
	double z=get(y,d,op3);
	
	return z;
}

double way3(double a,double b, double c, double d,char op1,char op2,char op3)
{

	double x=get(b,c,op2);
	double y=get(x,d,op3);
	double z=get(a,y,op1);
	return z;
}

double way4(double a,double b, double c, double d,char op1,char op2,char op3)
{
	double x=get(c,d,op3);
	double y=get(b,x,op2);
	double z=get(a,y,op1);
	
	return z;
}

double way5(double a,double b, double c, double d,char op1,char op2,char op3)
{
	double x=get(a,b,op1);
	double y=get(c,d,op3);
	double z=get(x,y,op2);
	
	return z;
}


int cal(int a,int b,int c,int d)//枚举运算符 可以重复
{

	for(int i=0;i<4;i++)
	{
		for(int j=0;j<4;j++)
		{
			for(int k=0;k<4;k++)
			{
				char op1=op[i],op2=op[j],op3=op[k];
				
				
				
				if(way1(a,b,c,d,op1,op2,op3)==24)
				{
				
					cout<<"("<<"("<<a<<op1<<b<<")"<<op2<<c<<")"<<op3<<d;
					return 0;
				}
				if(way2(a,b,c,d,op1,op2,op3)==24)
				{
					cout<<"("<<a<<op1<<"("<<b<<op2<<c<<")"<<")"<<op3<<d;
					return 0;
				}
				if(way3(a,b,c,d,op1,op2,op3)==24)
				{
					cout<<a<<op1<<"("<<"("<<b<<op2<<c<<")"<<op3<<d<<")";
					return 0;
				}
				if(way4(a,b,c,d,op1,op2,op3)==24)
				{
					cout<<a<<op1<<"("<<b<<op2<<"("<<c<<op3<<d<<")"<<")";
					return 0;
				}
				if(way5(a,b,c,d,op1,op2,op3)==24)
				{
					cout<<"("<<a<<op1<<b<<")"<<op2<<"("<<c<<op3<<d<<")";
					return 0;
				}
		
			}
		}
	}
	
	
	return 1;
	
}






void solve()
{
	int num[4]={0};
	for(int i=0;i<4;i++) cin>>num[i];

	do{
		int a=num[0],b=num[1],c=num[2],d=num[3];
	//	cout<<a<<" "<<b<<" "<<c<<" "<<d<<" "<<'\n';
	
		if(!cal(a,b,c,d)){
			return ;
		}
		
		
	}while(next_permutation(num,num+4));
	
		cout<<-1;
	
	
	
}

考点:二叉树

7-12 玩转二叉树
经典的中序前序建立树,这种题可以当板子了已经,按照题意建完树以后,遍历一下这颗树,只要不是叶子结点,交换即可

中序 前序建立树,可以先在纸上画出来,本质上是一个递归的过程。代码上先通过遍历中序找到前序中当前元素的位置,然后从这里将中序分为左右子树,不断递归即可,边界可以直接记口诀:左前-中+i

vector<int>tr[500];

int n;
int fd[35],od[35];

int  build (int l1,int r1,int l2,int r2)//l1,r1对应中序  l2,r2对应前序
{
	for(int i=l1;i<=r1;i++)//从中序去找前序
	{
		if(fd[l2]==od[i]){
			
			int root=od[i];
		int lc=	build(l1,i-1,l2+1,l2+i-l1);//前-中+i
		int rc=	build(i+1,r1,l2+i-l1+1,r2);
			
			if(lc) tr[root].push_back(lc);
			if(rc) tr[root].push_back(rc);
			
			return root;
			
		}
		
	}
	
	return 0;
}

void dfs(int x){
	if(tr[x].size()==0) return ;
	else {
		for(auto i:tr[x]){
			dfs(i);
		}
		
		if(tr[x].size()==2)	{
			swap(tr[x][0],tr[x][1]);

		}
	}

	
}


void solve()
{
	cin>>n;
	for(int i=1;i<=n;i++)  cin>>od[i];
	for(int i=1;i<=n;i++) cin>>fd[i];
	build(1,n,1,n);


	
	dfs(fd[1]);
	queue<int>q;
	q.push(fd[1]);
	cout<<fd[1];
	while(q.size())
	{
		int k=q.front();
		q.pop();
		for(auto i:tr[k]){
			q.push(i);
			cout<<" "<<i;
		}
		
	}
	
	
}

考点:中位数定理

中位数定理:给定一个数组,每次操作加1或者减1,将所有元素变成相同的最小操作次数则是将所有元素变成中位数即可

双生双宿之错
排完序后,记录前一半和后一半的中位数,计算前后部分变为中位数的次数之和即可,但是要特判前后中位数相等的情况,解决方法就是取前部分中位数-1,或后部分的中位数+1的最少总次数即可



void solve()
{
	int n; cin>>n;
	vector<int>ve(n+1);
	for(int i=1;i<=n;i++)  cin>>ve[i];
	
	sort(ve.begin(),ve.end());
	int ans=0;
	int k1=0,k2=0;
	if(n%4==0)
	{
		 k1=(ve[n/4]+ve[n/4+1])/2;
		 k2=(ve[n/2+n/4]+ve[n/4+n/2+1])/2;
	}else {
		k1=ve[n/4+1];
		k2=ve[n/4+n/2+1];
	}
	
	auto cal=[&](int a,int b)->int{
			int res=0;
		for(int i=1;i<=n;i++)
		{
			if(i<=n/2) res+=abs(ve[i]-a);
			else res+=abs(ve[i]-b);
		}
		return res;
	};
	
	if(k1!=k2) cout<<cal(k1,k2)<<"\n";
	else cout<<min(cal(k1-1,k1),cal(k1,k1+1))<<"\n";

	

}




考点:暴力优化处理

数值膨胀之美

这题的题解其实是相当妙的
我们知道极差就是max-min,那么在我们选择区间去把数乘2的时候,每次状态的最大值和最小值都可能发生改变,所以问题就是如何处理每个状态的最大值和最小值

我们就从最小值开始找,然后找次小值,次次小值,以此类推

记录数组元素和下标以后,对元素进行升序排序,此时我们计算第一次极差,此时取最大值就是\(max(a[0]*2,a[n-1])\),此时的最小值就是\(min(a[0]*2,a[1])\),那么下次依次类推就行了

最本质的就是,线性的去扩大这个每次找第k+1个最小值的区间,两个指针l与r线性的扩大到整个区间完

void solve()
{
    int n; cin>>n;
    vector<pii>ve(n);
    vector<int>a(n);
 
    for(int i=0;i<n;i++) {
        cin>>ve[i].first;
        ve[i].second=i;
        a[i]=ve[i].first;
    }
     
  
     
    sort(ve.begin(),ve.end());
     
     
      
     
    int ma=max(ve[n-1].first,ve[0].first*2);//最小值翻倍以后可能变成最大值
     
     ve.push_back({2e9,n});
    int res=ma-min(ve[0].first*2,ve[1].first);
     
      
     
    int l=ve[0].second,r=ve[0].second;
    for(int i=1;i<n;i++)
    {
        while(ve[i].second<l ){
            l--;
            ma=max(ma,a[l]*2);
        }
         
        while(ve[i].second>r){
            r++;
            ma=max(ma,a[r]*2);
        }
         
        res=min(ma-min(ve[0].first*2,ve[i+1].first),res);   
    }   
     
    cout<<res;
     
}
 

posted on 2025-01-26 03:34  swj2529411658  阅读(31)  评论(0)    收藏  举报

导航