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

比赛链接:这里

OP

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

A 股神

贪心 / DP

思路

朴素的贪心思想:

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

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

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

代码
#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 酒馆决斗

模拟

思路

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

代码
#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方程 d p [ i ] [ j ] = m a x ( d p [ i − 1 ] [ ( j − g % k + k ) % k ] + g , d p [ i − 1 ] [ j ] ) dp[i][j]=max(dp[i-1][(j-g\%k+k)\%k]+g,dp[i-1][j]) dp[i][j]=max(dp[i1][(jg%k+k)%k]+g,dp[i1][j]) d p [ i ] [ j ] dp[i][j] dp[i][j]代表有前 i 个数时, m o d ( k ) = j mod( k) = j mod(k)=j 时的最大和;

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

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

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

注意dp初始值的设定。

代码
#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为排序依据,降序排列;

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

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

代码
#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 < = 1 e 9 1<=n<=1e9 1<=n<=1e9
1 < = m < = 1 e 5 1<=m<=1e5 1<=m<=1e5
o p ∈ { 1 , 2 , 3 } op\in\{1,2,3\} op{1,2,3}
1 < = a < = b < = 1 e 9 1<=a<=b<=1e9 1<=a<=b<=1e9

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

F 幸运数字

暴力模拟

思路

暴力模拟即可。

代码
#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,%%%。

代码
#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;

注意处理边界。

代码
#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

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

评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值