Contest3395 - 2024寒假集训 进阶训练赛 (十二)

问题 A: 珠宝

题目描述

你的朋友送了你一个序列D
这个序列D上一共有n个珠宝
按照从左到右的顺序
每个珠宝都有它的价值w[i]
1 <= n <= 50
-1e7 <= w[i] <= 1e7
你可以对这些珠宝进行 最多 k次操作 
1 <= k <= 100
每次操作你都可以

操作A:取出D中最左边的宝石,放在手中。当D为空时,不能执行此操作。

操作B:取出D中最右边的宝石,放在手中。当D为空时,不能执行此操作。

操作C:选择手中的任意一个宝石并将其插入D的左端。如果手中没有宝石,则无法执行此操作。

操作D:选择手中的任意一个宝石并将其插入D的右端。如果手中没有宝石,则无法执行此操作。

求执行完 最多k次操作之后 你手上有的珠宝的价值之和的最大值

样例输入 
6 4
-10 8 2 1 2 6
样例输出 
14
思路分析

左边拿i个,右边拿n-j个,如果拿的个数超过k了,结束此次循环;否则若还有次数可用并且手中有负值,将负值放回,每次更新最大值

AC代码
#include<iostream>
#include<algorithm>
#include<math.h>
#include<set>
using namespace std;
int main()
{
	int n,k;
	cin>>n>>k;
	int a[55];
	for(int i=0;i<n;i++)
	cin>>a[i];
	int sum=0;
	for(int i=0;i<n;i++)//左边拿0-i 
	{
		for(int j=n;j>=i;j--)//右边拿j-(n-1) 
		{
			if(i+n-j>k) continue;
			multiset<int>mse;
			int su=0;//目前总价值 
			for(int k=0;k<i;k++)
			{
				mse.insert(a[k]);
				su+=a[k];
			}
			for(int k=n-1;k>=j;k--)
			{
				mse.insert(a[k]);
				su+=a[k];
			}
			int p=k-i-n+j;
			while(p>0&&*mse.begin()<0)//还有次数可用并且目前手里有负值 
			{
				su-=*mse.begin();
				mse.erase(mse.begin());
				p--;
			}
			sum=max(sum,su);
		}
	}
	cout<<sum<<endl;
	return 0;
}

问题 B: 1.5 哥德巴赫猜想

题目描述

哥德巴赫猜想:任一大于 2 的偶数,都可表示成两个素数之和。

验证:2000 以内大于 2 的偶数都能够分解为两个素数之和。

思路分析

暴力即可

AC代码
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
using namespace std;
bool iss(int x)
{
	if(x==1||x==4) return false;
	if(x==2||x==3) return true;
	int q=sqrt(x);
	for(int i=2;i<=q;i++)
	{
		if(x%i==0)
		return false;
	}
	return true;
}
signed main()
{
	for(int i=4;i<=2000;i+=2)
	{
		for(int j=2;j<=i;j++)
		{
			if(iss(j)&&iss(i-j))//判断两个加数为素数 
			{
				cout<<i<<"="<<j<<"+"<<i-j<<endl;
				break;	//要求输出第一个值最小,找到就退出 
			}
		}
	}
	return 0;
}

问题 C: 你的名字 

emmm……题解没太看懂 先放这了2021-2022-1 ACM集训队每周程序设计竞赛(9) - 问题 F: 你的名字- 题解_本题讨论范围是二维坐标平面。 集合s ss有n nn个点,第i ii个点的坐标是( x i , y-CSDN博客

问题 D: 4.8 小石子游戏 

题目描述

一群小孩子在玩小石子游戏,游戏有两种玩法。
(1)路边玩法
有n堆石子堆放在路边,现要将石子有序地合并成一堆,规定每次只能移动相邻的两堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费(最小或最大)。
(2)操场玩法
一个圆形操场周围摆放着n堆石子,现要将石子有序地合并成一堆,规定每次只能移动相邻的两堆石子合并,合并花费为新合成的一堆石子的数量。求将这N堆石子合并成一堆的总花费(最小或最大)。

样例输入 
1
6
5 8 6 9 2 3
样例输出 
84 129 81 130
思路分析 

dp问题,mi[i][j]和ma[i][j]分别表示将第i堆石子到第j堆石子合并在一起的最小最大花费,注意合并石子的花费为前半部分+后半部分+最初整段的价值

AC代码
#include<iostream>
#include<algorithm>
#include<string.h>
#define int long long
using namespace std;
//注意在求解圆形的时候所需长度为2*n,不然会RE ┭┮﹏┭┮ 
int mi[205][205]={0};
int ma[205][205]={0};
int s[205]={0};
int a[205];
signed main()
{
	int t;
	cin>>t;
	while(t--)
	{
		int n;
		cin>>n;
		memset(mi,1000000,sizeof mi);
		memset(ma,-1,sizeof ma);
		//直线 
		for(int i=1;i<=n;i++)
		{
			cin>>a[i];
			s[i]=s[i-1]+a[i];
			mi[i][i]=0;
			ma[i][i]=0;
		}
		for(int i=1;i<n;i++)
		{
			for(int j=1;j+i<=n;j++)
			{
				for(int k=j;k<j+i;k++)
				{
					mi[j][i+j]=min(mi[j][i+j],mi[j][k]+mi[k+1][i+j]+s[i+j]-s[j-1]);
					ma[j][j+i]=max(ma[j][i+j],ma[j][k]+ma[k+1][i+j]+s[i+j]-s[j-1]);
				}
			}
		}
        //直接输出第1堆石子到第n堆石子的最大最小价值
		cout<<mi[1][n]<<" "<<ma[1][n]<<" ";
		
		
		//圆形-将直线前n-1再接到直线后面 
		for(int i=1;i<n;i++)
		{
			a[i+n]=a[i];
			s[i+n]=s[i+n-1]+a[i];
			mi[i+n][i+n]=ma[i+n][i+n]=0;
		}
		for(int i=1;i<n;i++)
		{
			for(int j=1;j+i<=2*n-1;j++)
			{
				for(int k=j;k<j+i;k++)
				{
					mi[j][i+j]=min(mi[j][i+j],mi[j][k]+mi[k+1][i+j]+s[i+j]-s[j-1]);
					ma[j][j+i]=max(ma[j][i+j],ma[j][k]+ma[k+1][i+j]+s[i+j]-s[j-1]);
				}
			}
		}
        //圆形可以从第i堆石子合并到第i-1堆石子,所以要循环一次
		int mii=10000000,maa=-100000000;
		for(int i=1;i<=n;i++)
		{
			mii=min(mii,mi[i][i+n-1]);
			maa=max(maa,ma[i][i+n-1]);
		}
		cout<<mii<<" "<<maa<<endl;
	}
}

问题 E: Bitset 

题目描述

将十进制数字转换为二进制数字输出

样例输入 
7
样例输出 
111
思路分析 

用栈存储计算结果,再依次输出即可

AC代码
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<stack> 
using namespace std;
signed main()
{
	int n;
	cin>>n;
	stack<int>st;
	while(n)
	{
		st.push(n%2);
		n/=2;
	}
	while(st.size())
	{
		cout<<st.top();
		st.pop();
	}
	cout<<endl;
	return 0;
}

 问题 F: 2.4.5 度度熊学队列

题目描述

度度熊正在学习双端队列,他对其翻转和合并产生了很大的兴趣。 初始时有 N 个空的双端队列(编号为 1 到 N ),你要支持度度熊的 Q 次操作。

①1 u w val 在编号为 u 的队列里加入一个权值为 val 的元素。(w=0表示加在最前面,w=1 表示加在最后面)。

②2 u w 询问编号为 u 的队列里的某个元素并删除它。( w=0 表示询问并操作最前面的元素,w=1 表示最后面)

③3 u v w 把编号为 v 的队列“接在”编号为 u 的队列的最后面。w=0 表示顺序接(队列 v 的开头和队列 u 的结尾连在一起,队列v 的结尾作为新队列的结尾), w=1 表示逆序接(先将队列 v 翻转,再顺序接在队列 u 后面)。且该操作完成后,队列 v 被清空。

样例输入 
2 10
1 1 1 23
1 1 0 233
2 1 1 
1 2 1 2333
1 2 1 23333
3 1 2 1
2 2 0
2 1 1
2 1 0
2 1 1
样例输出 
23
-1
2333
233
23333
思路分析

直接用双端队列操作

AC代码
#include<iostream>
#include<algorithm>
#include<deque>
using namespace std;
deque<int>de[150005];
int main()
{
	int n,q;
	scanf("%d%d",&n,&q);
	while(q--)
	{
		int x;
		int u,w,val,v;
		scanf("%d",&x);
		if(x==1)
		{
			scanf("%d%d%d",&u,&w,&val);
			if(w==1) de[u].push_back(val);//尾插 
			else de[u].push_front(val);//头插 
		}
		else if(x==2)
		{
			scanf("%d%d",&u,&w);
			if(de[u].size()==0)
			printf("-1\n");
			else
			{
				if(w==1)
				{
					printf("%d\n",de[u].back());//最后一个元素 
					de[u].pop_back();//尾删 
				}
				
				else
				{
					printf("%d\n",de[u].front());//第一个元素 
					de[u].pop_front();//头删 
				}
				
			}
		}
		else
		{
			scanf("%d%d%d",&u,&v,&w);
			if(w==1) de[u].insert(de[u].end(),de[v].rbegin(),de[v].rend());//逆序接 
			else de[u].insert(de[u].end(),de[v].begin(),de[v].end());//顺序接 
			de[v].clear();//注意要清空 
		}
	}
}

问题 G: [蓝桥杯2016初赛]密码脱落 

题目描述

X星球的考古学家发现了一批古代留下来的密码。这些密码是由A、B、C、D 四种植物的种子串成的序列。
仔细分析发现,这些密码串当初应该是前后对称的(也就是我们说的镜像串)。
由于年代久远,其中许多种子脱落了,因而可能会失去镜像的特征。
你的任务是:给定一个现在看到的密码串,计算一下从当初的状态,它要至少脱落多少个种子,才可能会变成现在的样子。

样例输入 
ABCBA
ABDCDCBABC
样例输出 
0
3
思路分析

对于每一个密码,脱落的长度=原长-原字符串与逆序字符串的最长公共子串长度,最长公共子串不要求连续。dp求最长公共子串,dp[i][j]表示原字符串截止到i位置,逆序字符串截至到j位置的最长公共子串

AC代码
#include<iostream>
#include<algorithm>
using namespace std;
int dp[1005][1005];
int main()
{
	string s;
	while(cin>>s)
	{
		string s0=s;
		reverse(s0.begin(),s0.end());
		int n=s.size();
		for(int i=0;i<=n;i++)
		{
			dp[i][0]=0;
			dp[0][i]=0;
		}
		for(int i=1;i<=n;i++)
		{
			for(int j=1;j<=n;j++)
			{
				if(s[i-1]==s0[j-1]) 
				{
					dp[i][j]=dp[i-1][j-1]+1;
				}
				else
				{
					dp[i][j]=max(dp[i-1][j],dp[i][j-1]);
				}
			}
		}
		cout<<n-dp[n][n]<<endl;
	}
}

 问题 H: [蓝桥杯2016初赛]平方怪圈

题目描述

如果把一个正整数的每一位都平方后再求和,得到一个新的正整数。对新产生的正整数再做同样的处理。
如此一来,你会发现,不管开始取的是什么数字,最终如果不是落入1,就是落入同一个循环圈。
请写出这个循环圈中最大的那个数字。 

思路分析

找一个数自己试试就行了。比如我找的123-14-17-50-25-19-90-81-65-61-37-58-89-145-42-20-4-16-37……到37开始循环,所以最大的是145

问题 I: 比大小

题目描述

小A和小B各自拥有两个正整数a和b,1≤a,b≤9 。
他们不了解字典序是如何比较的,所以他们构造了两个字符串:
一个由a构成,长度为b;另一个由b构成,长度为a
请输出字典序较小的那一个。

样例输入 
4 3
样例输出 
3333
思路分析

由于按字典序,则输出max(a,b)个min(a,b)即可

AC代码
#include<iostream>
#include<algorithm>
#include<string.h>
#include<math.h>
#include<stack> 
using namespace std;
signed main()
{
	int a,b;
	cin>>a>>b;
	int p,q;
	p=min(a,b);
	q=max(a,b);
	for(int i=0;i<q;i++)
	cout<<p;
	cout<<endl;
	return 0;
}

问题 J: 天上天下,唯我独尊

题目描述

作为爽文小说中的主角,必须是所有人物中最强的。
现在有n位人物,第i位的等级为 S[i],按照下标顺序依次出场。
如果当前出场人物为全场最强时,他就是现阶段的主角。
请问最多有多少位主角?(真不值钱)
等级值越小,能力越强

样例输入 
6
1 2 3 4 5 6
样例输出 
1
思路分析

每次比较当前值与最小值的关系,若小于最小值,则总数加一,更新最小值

AC代码
#include<iostream>
#include<algorithm>
using namespace std;
int a[200005];
int main()
{
	int n;
	cin>>n;
	int su=0,mi;
	cin>>a[0];
	su++,mi=a[0];
	for(int i=1;i<n;i++)
	{
		cin>>a[i];
		if(a[i]<mi)
		{
			su++;
			mi=a[i];
		}
	}
	cout<<su<<endl;
 }

问题 K: 大聪明虹村亿泰

题目描述

替身【轰炸空间】的能力是斩断空间,因此虹村亿泰想要使用替身能力在马拉松中搞事情。
他可以从点0或者点1开始向x正方向移动,初始耗费的体力值均为1
每次他可以耗费能力向前移动两格,同时耗费的体力也会增加,具体为f(n)=n∗f(n−2),n≥2
那么问题来了,虹村亿泰到达N点耗费的体力值……有多少个后缀0呢?

样例输入 
13
样例输出 
0
思路分析 

后缀零由2*5形成,当n为奇数时,f(n)中不含2的因子,所以答案为0,而当n为偶数时,f(n)=2*4*6*……*n=(n/2)!*2^(n/2),可见因子2的个数要远大于5的个数,所以只需其因子5的个数即为后缀零的个数

AC代码
#include<iostream>
#include<algorithm>
#define int long long
using namespace std;
signed main()
{
	int n;
	cin>>n;
	if(n%2==1)
	{
		cout<<"0"<<endl;
		return 0;
	}
	int su=0;
	n/=2;
	while(n>=5)
	{
		su+=n/5;
		n/=5;
	}
	cout<<su<<endl;
}

  • 30
    点赞
  • 38
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值