2021年东北林业大学蓝桥杯选拔赛(软件类)(A B C D F G H)

比赛链接:这里

OP

E的知识点好像需要补,赛时想到的都爆TLE

A 股神

贪心 / DP

思路

朴素的贪心思想:

次日若比此日股价高,且没有买入,则所有金币买入;
次日若比此日股价低,且有买入,则全部卖出;

注意输出时转换成金币输出;
注意浮点误差;
注意第0天也可以进行买入操作。

DP方法可以参考ph大佬的题解

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M=1e9+7;
const int INF=0x3f3f3f3f;

int main()
{
double gp[502],w=1,g=0;
int n,i;
cin>>n;
for(i=1;i<=n;i++)scanf("%lf",&gp[i]);
gp[0]=1;//第0天股价
for(i=0;i<n;i++)
{
if(g==0)
{
if(gp[i+1]>gp[i])
{
g=w/gp[i];
w=0;
}
}
else
{
if(gp[i+1]<gp[i])
{
w=g*gp[i];
g=0;
}
}
}
if(g>0)w+=g*gp[n];//转换为金币,防浮点误差
printf("%.2lf",w);
return 0;
}

B 酒馆决斗

模拟

思路

排序后进行模拟,注意每次出招都有可能造成对方减员,而不是每回合;

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M=1e9+7;
const int INF=0x3f3f3f3f;

struct qd
{int a,b;}qua[100005],shp[100005];
bool cmp(qd x,qd y)//排序
{
if(x.a==y.a)return x.b>y.b;
return x.a>y.a;
}
int main()
{
int n,m,i,j;
cin>>n>>m;
for(i=1;i<=n;i++)scanf("%d%d",&qua[i].a,&qua[i].b);
for(i=1;i<=m;i++)scanf("%d%d",&shp[i].a,&shp[i].b);
sort(qua+1,qua+1+n,cmp);
sort(shp+1,shp+1+m,cmp);
i=j=1;
for(;i<=n&&j<=m;)
{
shp[j].b-=qua[i].a;
if(shp[j].b<=0)j++;
if(j>m)break;
qua[i].b-=shp[j].a;
if(qua[i].b<=0)i++;
}
if(i<=n)printf("Quasrain");else printf("SHP");
printf(" wins, left ");
printf("%d",max(m-j+1,n-i+1));
return 0;
}

C 求 k 整除最大元素和

DP

思路

建立dp方程dp[i][j]=max(dp[i1][(jg%k+k)%k]+g,dp[i1][j])dp[i][j]=max(dp[i-1][(j-g\%k+k)\%k]+g,dp[i-1][j])dp[i][j]dp[i][j]代表有前 i 个数时,mod(k)=jmod( k) = j 时的最大和;

[(jg%k+k)%k][(j-g\%k+k)\%k]是为了防止计算后的余数变负或溢出;

赛时没找到简单的压缩至一维dp的方法(实际上应该有),而开[102][100005]会MLE;
因为处理 [i] 时只需要 [i] 与 [i-1] 行数数据;
所以通过开 [2][100005] ,并在 [1] 每一行处理完后,将数据更新[0]内,以节省内存;

数据最大和为1e14,可以放心大胆地用 ll 进行dp;

注意dp初始值的设定。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M=19260817,N=1e9+9;
const int INF=0x3f3f3f3f;

ll dp[2][102]={0};
int main()
{
int n,k,i,j,g;
cin>>n>>k;
for(i=1;i<k;i++)dp[0][i]=-1e14-1;//初值
for(i=1;i<=n;i++)
{
scanf("%d",&g);
for(j=0;j<k;j++)
{
dp[1][j]=max(dp[0][(j-g%k+k)%k]+g,dp[0][j]);
}
for(j=0;j<k;j++)//更新数据至dp[0]
{
dp[0][j]=dp[1][j];
}
}
printf("%lld",(dp[0][0])>0?dp[0][0]%M:0);
return 0;
}

D 取快递

贪心

思路

以a为排序依据,降序排列;

从用时最长的开始,直至超能力时间和大于该快递用时,或该快递的超能力用时大于标准用时时,跳出贪心;

跳出后比较标准用时与超能力用时和的最大值输出。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M=1e9+7;
const int INF=0x3f3f3f3f;

struct qd
{int a,b;}qdb[100005];//快递表
bool cmp(qd x,qd y)
{
if(x.a==x.b)return x.b<y.b;
return x.a<y.a;
}
int main()
{
int n,i,j,tb=0;
cin>>n;
for(i=1;i<=n;i++)scanf("%d%d",&qdb[i].a,&qdb[i].b);
sort(qdb+1,qdb+1+n,cmp);
for(i=n;i>=1;i--)
{
if(qdb[i].b>qdb[i].a)break;//此快递超能力用时大于标准用时
else
{
if(tb+qdb[i].b<=qdb[i].a)tb+=qdb[i].b;
else break;//超能力取此快递前超能力用时和大于此快递标准用时
}
if(tb>=qdb[i].a)break;//超能力取此快递后超能力用时和大于此快递标准用时
}
printf("%d",max(tb,qdb[i].a));
return 0;
}

数组与指令

原题被删除了,在这里大概描述一下:

题目描述

有一长度为n的数组,初始值全为0,现对其进行m此操作:
每次操作遵循op a b的输入形式

若op为1,则对第a位的数加b;
若op为2,则输出数组[a,b]区间上的非零数个数;
若op为3,则输出数组[a,b]区间上数字之和;

输入共m+1行,第一行输入n,m;
接下来m行输入m次操作;

对于每一次操作2或操作3,输出一行答案;

数据范围:
1<=n<=1e91<=n<=1e9
1<=m<=1e51<=m<=1e5
op{1,2,3}op\in\{1,2,3\}
1<=a<=b<=1e91<=a<=b<=1e9

大概是这个样的,目前还没什么好想法。

F 幸运数字

暴力模拟

思路

暴力模拟即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M=1e9+7;
const int INF=0x3f3f3f3f;

int main()
{
int m,i,j,ans=0;
cin>>m;
for(i=1;i<=m;i++)
{
int sum=0;
j=i;
while(j)
{
sum+=j%10;
j/=10;
}
if(i%sum==1)ans++;
}
cout<<ans;
return 0;
}

G 有限小数

进制转换

思路

可以参考浮点数进制转换

我先将获得的A进制数转换为10进制,再进行处理,方便操作与理解。

对于进制转换,我们可以不关心转换成的B进制数具体是什么,只关心是否有剩余;

所以我们就可以暴力地进行足够多位的转换,直接看结果是否有剩余即可。

关于进一步优化可以参考ph大佬的题解
PS:ph大佬在进制转换的字符串遍历方向错了的情况下依然过掉了9/10,%%%。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M=19260817,N=1e9+9;
const int INF=0x3f3f3f3f;

ll ctoten(string& n,ll a)//change to ten
{
//cout<<n<<endl;
ll ans=0,ma=1;
for(int i=n.length()-1;i>=0;i--)
{
ans+=(n[i]>='0'&&n[i]<='9')?(n[i]-'0')*ma:(n[i]-'A'+10)*ma;
ma*=a;
}
//printf("%lld\n",ans);
return ans;
}
int main()
{
ll a,b,nu,nd,i;//numberup,numberdown
string up,down;
cin>>up>>down>>a>>b;
nu=ctoten(up,a);
nd=ctoten(down,a);
if(nu>=nd)nu-=nd;
//printf("%lld %lld\n",nu,nd);
for(i=1;i<=10000000;i++)//1e7位
{
nu*=b;
nu%=nd;
}
if(!nu)printf("Yes");//分子为0即无剩余
else printf("No");
return 0;
}

H 子数组和

滑窗

思路

从右界累加至和不小于 k ,再从左界累减至不大于k;

若此时和值为k,则输出左右界;
否则即再执行上面的操作;

若遍历完都没有和为k的情况,则输出-1;

注意处理边界。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll M=1e9+7;
const int INF=0x3f3f3f3f;

int main()
{
ll n,a[1003],k,i,j,sum;
cin>>n>>k;
for(i=1;i<=n;i++)scanf("%lld",&a[i]);
i=1,j=0,sum=0;
for(;i<=n&&j<=n;)
{
while(sum<k&&j<=n)j++,sum+=a[j];//移动右界
while(sum>k&&i<=n)sum-=a[i],i++;//移动左界
if(sum==k)break;
}
if(j==n+1||i==n+1)printf("-1");//边界溢出则无子数组满足条件
else printf("%lld %lld",i,j);
return 0;
}

ED

码字较急,难免有疏漏与错误,欢迎评论区讨论与反馈。


2021年东北林业大学蓝桥杯选拔赛(软件类)(A B C D F G H)
https://tanyuu.github.io/2021.01-06/2021年东北林业大学蓝桥杯选拔赛(软件类)(A B C D F G H)/
作者
F Juny
发布于
2021年3月13日
许可协议