问题 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;
}