第十四届蓝桥杯大赛软件赛省赛 Java 大学 B 组题解

试题 A: 阶乘求和
本题总分:5 分
【问题描述】
令 S = 1! + 2! + 3! + ... + 202320232023!,求 S 的末尾 9 位数字。
提示:答案首位不为 0。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

找规律,可以先手动模拟几次,会发现n! 随着n越大,零也越多,当n为40的时候刚好有9个0

所以到40项以后的末尾9个阶乘的和一定是不变的,可以用手算,也可以写程序

答案是,901327897

代码:

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=110000;

int f[N];

signed main()
{
    ios::sync_with_stdio(false);
    f[1]=1;
    for(int i=2;i<=50;i++)
        f[i]=f[i-1]*i;
    for(int i=2;i<=50;i++)
        f[i]=f[i-1]+f[i];
    cout<<f[40]%1000000000;
    return 0;
}
试题 B: 幸运数字
本题总分:5 分
【问题描述】
哈沙德数是指在某个固定的进位制当中,可以被各位数字之和整除的正整
数。例如 126 是十进制下的一个哈沙德数,因为 (126)10 mod (1+2+6) = 0;126
也是八进制下的哈沙德数,因为 (126)10 = (176)8,(126)10 mod (1 + 7 + 6) = 0;
同时 126 也是 16 进制下的哈沙德数,因为 (126)10 = (7e)16,(126)10 mod (7 +
e) = 0。小蓝认为,如果一个整数在二进制、八进制、十进制、十六进制下均为
哈沙德数,那么这个数字就是幸运数字,第 1 至第 10 个幸运数字的十进制表示
为:1 , 2 , 4 , 6 , 8 , 40 , 48 , 72 , 120 , 126 . . . 。现在他想知道第 2023 个幸运数
字是多少?你只需要告诉小蓝这个整数的十进制表示即可。
【答案提交】
这是一道结果填空的题,你只需要算出结果后提交即可。本题的结果为一
个整数,在提交答案时只填写这个整数,填写多余的内容将无法得分。

Java中有十进制转化为二进制,十六进制,八进制的方法,暴力枚举一下即可。(因为不会Java所以懒得写了)

试题 C: 数组分割
时间限制: 1.0s 内存限制: 512.0MB 本题总分:10 分
【问题描述】
小蓝有一个长度为 N 的数组 A = [A0, A1, . . . , AN−1]。现在小蓝想要从 A 对
应的数组下标所构成的集合 I = {0, 1, 2, . . . , N − 1} 中找出一个子集 R1,那么 R1
在 I 中的补集为 R2。记 S 1 =
∑
r∈R1
Ar,S 2 =
∑
r∈R2
Ar,我们要求 S 1 和 S 2 均为
偶数,请问在这种情况下共有多少种不同的 R1。当 R1 或 R2 为空集时我们将
S 1 或 S 2 视为 0。
【输入格式】
第一行一个整数 T,表示有 T 组数据。
接下来输入 T 组数据,每组数据包含两行:第一行一个整数 N,表示数组
A 的长度;第二行输入 N 个整数从左至右依次为 A0, A1, . . . , AN−1,相邻元素之
间用空格分隔。
【输出格式】
对于每组数据,输出一行,包含一个整数表示答案,答案可能会很大,你
需要将答案对 1000000007 进行取模后输出。
【样例输入】
2
2
6 6
2
1 6
【样例输出】
4
试题C: 数组分割 4
第十四届蓝桥杯大赛软件赛省赛 Java 大学 B 组
0
【样例说明】
对于第一组数据,答案为 4。(注意:大括号内的数字表示元素在数组中的
下标。)
R1 = {0}, R2 = {1};此时 S 1 = A0 = 6 为偶数, S 2 = A1 = 6 为偶数。
R1 = {1}, R2 = {0};此时 S 1 = A1 = 6 为偶数, S 2 = A0 = 6 为偶数。
R1 = {0, 1}, R2 = {};此时 S 1 = A0 + A1 = 12 为偶数, S 2 = 0 为偶数。
R1 = {}, R2 = {0, 1};此时 S 1 = 0 为偶数, S 2 = A0 + A1 = 12 为偶数。
对于第二组数据,无论怎么选择,都不满足条件,所以答案为 0。
【评测用例规模与约定】
对于 20% 的评测用例,1 ≤ N ≤ 10。
对于 40% 的评测用例,1 ≤ N ≤ 102。
对于 100% 的评测用例,1 ≤ T ≤ 10, 1 ≤ N ≤ 103
, 0 ≤ Ai ≤ 109。

做法:线性dp

先看思路,当数组总和为奇数的时候一定不存在R1和R2,因为R2是R1的补集,当总和为奇数的时候,R1和R2的奇偶性一定不同,因为奇数加偶数才是奇数。

这样我们就可以定义状态

f[i][j]表示的是当前是第i个元素,并且奇偶性是j

其中f[i][0]表示的是当前第i个元素为偶数,f[i][1]当前第i个元素是奇数

答案就是\sum_{i=1}^{n}f[i][0]

状态转移方程:

如果a[i]是偶数那么就有

f[i][0]=f[j][0]+f[i][0]

因为偶数加偶数还是偶数

f[i][1]=f[i][1]+f[j][1]

奇数加奇数还是偶数

如果a[i]是奇数那么就有

f[i][1]=f[i][1]+f[j][0]

 因为奇数加偶数还是奇数下面同理

f[i][0]=f[i][0]+f[j][1]

即可求解出答案,以下是代码

#include<bits/stdc++.h>
#define int long long
#define x first
#define y second
using namespace std;
const int N=21000,mod=1000000007;

int n,a[N],s[N];
int f[N][3];

void solve()
{
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    memset(f,0,sizeof f);
    int sum=0;
    for(int i=1;i<=n;i++)
        sum+=a[i];
    if(sum%2)
    {
        cout<<0<<endl;
        return;
    }

    int ans=1;
    for(int i=1;i<=n;i++)
    {
        if(a[i]%2==0)
        {
            f[i][0]=1;
            for(int j=1;j<i;j++)
            {
                f[i][0]=(f[i][0]+f[j][0])%mod;
                f[i][1]=(f[i][1]+f[j][1])%mod;
            }
        }
        else
        {
            f[i][1]=1;
            for(int j=1;j<i;j++)
            {
                f[i][1]=(f[i][1]+f[j][0])%mod;
                f[i][0]=(f[i][0]+f[j][1])%mod;
            }
        }
        ans=(f[i][0]+ans)%mod;
    }
    cout<<ans<<endl;
}


signed main()
{
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--)
        solve();
    return 0;
}

 

试题 D: 矩形总面积
时间限制: 1.0s 内存限制: 512.0MB 本题总分:10 分
【问题描述】
平面上有个两个矩形 R1 和 R2,它们各边都与坐标轴平行。设 (x1, y1) 和
(x2, y2) 依次是 R1 的左下角和右上角坐标,(x3, y3) 和 (x4, y4) 依次是 R2 的左下
角和右上角坐标,请你计算 R1 和 R2 的总面积是多少?
注意:如果 R1 和 R2 有重叠区域,重叠区域的面积只计算一次。
【输入格式】
输入只有一行,包含 8 个整数,依次是:x1,y1,x2,y2,x3,y3,x4 和 y4。
【输出格式】
一个整数,代表答案。
【样例输入】
2 1 7 4 5 3 8 6
【样例输出】
22
【样例说明】
样例中的两个矩形如图所示:
试题 D: 矩形总面积 6
第十四届蓝桥杯大赛软件赛省赛 Java 大学 B 组
【评测用例规模与约定】
对于 20% 的数据,R1 和 R2 没有重叠区域。
对于 20% 的数据,其中一个矩形完全在另一个矩形内部。
对于 50% 的数据,所有坐标的取值范围是 [0, 103
]。
对于 100% 的数据,所有坐标的取值范围是 [0, 105
]。

思路一:扫描线模板

#include<bits/stdc++.h>
#define int long long
using namespace std;

const int N = 100010;

struct segment
{
    int x, y1,y2;
    int d;
    bool operator < (const segment&t)const
    {
        return x < t.x;
    }
}seg[N * 2];
struct node
{
    int l,r;
    int cnt;
    int len;
}tr[N * 8];
vector<int>ys;
int n;

int find(int y)
{
    return lower_bound(ys.begin(), ys.end(), y) - ys.begin();
}

void pushup(int u)
{

    if(tr[u].cnt)tr[u].len = ys[tr[u].r + 1] - ys[tr[u].l];
    else if(tr[u].l != tr[u].r)
    {
        tr[u].len = tr[u << 1].len + tr[u << 1 | 1].len;
    }
    else tr[u].len = 0;
}

void modify(int u,int l,int r,int d)
{
    if(tr[u].l >= l && tr[u].r <= r)
    {
        tr[u].cnt += d;
        pushup(u);
    }
    else
    {
        int mid = tr[u].r + tr[u].l >> 1;
        if (l <= mid)modify(u << 1,l,r,d);
        if (r > mid)modify(u << 1 | 1,l,r,d);
        pushup(u);
    }
}

void build(int u,int l,int r)
{
    tr[u] = {l,r,0,0};

    if (l != r)
    {
        int mid = l + r >> 1;
        build(u << 1,l,mid),build(u << 1 | 1,mid + 1,r);
    }
}

signed main()
{
    int T = 1;
    n=2;
    while (n)
    {
        ys.clear();
        int j = 0;
        for (int i = 0 ; i < n ; i ++)
        {
            int x1,y1,x2,y2;
            cin>>x1>>y1>>x2>>y2;
            seg[j ++] = {x1,y1,y2,1};
            seg[j ++] = {x2,y1,y2,-1};
            ys.push_back(y1),ys.push_back(y2);
        }
        sort(seg,seg + j);

        sort(ys.begin(),ys.end());
        ys.erase(unique(ys.begin(),ys.end()),ys.end());
        build(1,0,ys.size() - 2);

        int res = 0;

        for (int i = 0 ; i < j ; i ++)
        {
            if (i)res += tr[1].len * (seg[i].x - seg[i - 1].x);
            modify(1,find(seg[i].y1), find(seg[i].y2) - 1,seg[i].d);
        }

       cout<<res<<endl;
        break;
    }
    return 0;
}

思路二:用数学

如果两个矩形相交的话就将一个两个矩形补全成一个大矩形,然后减去两个多出来的小矩形即可。

代码就懒得写了

试题 E: 蜗牛
时间限制: 1.0s 内存限制: 512.0MB 本题总分:15 分
【问题描述】
这天,一只蜗牛来到了二维坐标系的原点。
在 x 轴上长有 n 根竹竿。它们平行于 y 轴,底部纵坐标为 0,横坐标分别
为 x1, x2, ..., xn。竹竿的高度均为无限高,宽度可忽略。蜗牛想要从原点走到第
n 个竹竿的底部也就是坐标 (xn, 0)。它只能在 x 轴上或者竹竿上爬行,在 x 轴
上爬行速度为 1 单位每秒;由于受到引力影响,蜗牛在竹竿上向上和向下爬行
的速度分别为 0.7 单位每秒和 1.3 单位每秒。
为了快速到达目的地,它施展了魔法,在第 i 和 i + 1 根竹竿之间建立了传
送门(0 < i < n),如果蜗牛位于第 i 根竹竿的高度为 ai 的位置 (xi
, ai),就可以
瞬间到达第 i + 1 根竹竿的高度为 bi+1 的位置 (xi+1, bi+1),请计算蜗牛最少需要
多少秒才能到达目的地。
【输入格式】
输入共 1 + n 行,第一行为一个正整数 n;
第二行为 n 个正整数 x1, x2, . . . , xn;
后面 n − 1 行,每行两个正整数 ai
, bi+1。
【输出格式】
输出共一行,一个浮点数表示答案(四舍五入保留两位小数)。
【样例输入】
3
1 10 11
1 1
2 1
试题E: 蜗牛 8
第十四届蓝桥杯大赛软件赛省赛 Java 大学 B 组
【样例输出】
4.20
【样例说明】
蜗牛路线:
(0, 0) → (1, 0) → (1, 1) → (10, 1) → (10, 0) → (11, 0),花费时间为 1 + 0
1
.7 +
0 + 1
1
.3 + 1 ≈ 4.20
【评测用例规模与约定】
对于 20% 的数据,保证 n ≤ 15;
对于 100% 的数据,保证 n ≤ 105,ai
, bi ≤ 104,xi ≤ 109。

思路:线性dp

定义状态f[i][j]表示的是当前所在的杆子是第i个杆子j表示的是,是否使用传送门

j=1表示用传送门,j=0表示不用传送门

状态转移方程就可以写成

f[i][0]=min(f[i-1][0]+x[i]-x[i-1],f[i-1][0]+b[i-1]/1.3)

当使用传送门的时候可以写成 

f[i][1]=min(f[i-1][0]+a[i]/0.7,f[i-1][1]+(b[i-1]>a[i])?(b[i-1]-a[i])/1.3:(a[i]-b[i-1])/0.7)

 其中(b[i-1]>a[i])?(b[i-1]-a[i])/1.3:(a[i]-b[i-1])/0.7)的意思表示的是

如果此时传送过来的位置高于在i出传送点的位置的时候就要滑下去此时速度是1.3

反之就要爬上去速度是0.7

答案就是f[n][0]

 代码

#include<bits/stdc++.h>
#define int long long
#define x first
#define y second
using namespace std;
const int N=110000;

int n;
double x[N],a[N],b[N];
double f[N][3];//f[i][1]传送门,f[i][0]

signed main() {
    ios::sync_with_stdio(false);
    cin >> n;
    for (int i = 1; i <= n; i++)
        cin >> x[i];

    for (int i = 1; i < n; i++)
        cin >> a[i] >> b[i];

    f[1][0] = x[1];
    f[1][1] = x[1] + a[1] / 0.7;

    for (int i = 2; i <= n; i++)
    {
        f[i][0]=min(f[i-1][0]+x[i]-x[i-1],f[i-1][1]+b[i-1]/1.3);
        f[i][1]=min(f[i][0]+a[i]/0.7,f[i-1][1]+((b[i-1]>a[i])?(b[i-1]-a[i])/1.3:(a[i]-b[i-1])/0.7));
    }
    printf("%.2lf",f[n][0]);

    return 0;
}
试题 G: 买二赠一
时间限制: 1.0s 内存限制: 512.0MB 本题总分:20 分
【问题描述】
某商场有 N 件商品,其中第 i 件的价格是 Ai。现在该商场正在进行 “买二
赠一” 的优惠活动,具体规则是:
每购买 2 件商品,假设其中较便宜的价格是 P(如果两件商品价格一样,
则 P 等于其中一件商品的价格),就可以从剩余商品中任选一件价格不超过 P
2
的商品,免费获得这一件商品。可以通过反复购买 2 件商品来获得多件免费商
品,但是每件商品只能被购买或免费获得一次。
小明想知道如果要拿下所有商品(包含购买和免费获得),至少要花费多少
钱?
【输入格式】
第一行包含一个整数 N。
第二行包含 N 个整数,代表 A1, A2, A3, . . . ,AN。
【输出格式】
输出一个整数,代表答案。
【样例输入】
7
1 4 2 8 5 7 1
【样例输出】
25
试题 G: 买二赠一 13
第十四届蓝桥杯大赛软件赛省赛 Java 大学 B 组
【样例说明】
小明可以先购买价格 4 和 8 的商品,免费获得一件价格为 1 的商品;再后
买价格为 5 和 7 的商品,免费获得价格为 2 的商品;最后单独购买剩下的一件
价格为 1 的商品。总计花费 4 + 8 + 5 + 7 + 1 = 25。不存在花费更低的方案。
【评测用例规模与约定】
对于 30% 的数据,1 ≤ N ≤ 20。
对于 100% 的数据,1 ≤ N ≤ 5 × 105,1 ≤ Ai ≤ 109。

思路:简单的贪心,每次取最大的两个然后免费其中两个中最小的/2就行

代码:

#include<bits/stdc++.h>
#define int long long
#define x first
#define y second
using namespace std;
const int N=510000;

int n;
int a[N];

int check()
{
    priority_queue<int>st;
    int res=0;
    int t=0;
    for(int i=n;i;i--)
    {
        if(!st.empty()&&st.top()>=a[i])
        {
            st.pop();
            continue;
        }
        res+=a[i];
        t++;
        if(t>=2){
            t=0;
            st.push(a[i]/2);
        }
    }
    return res;
}


signed main()
{
    ios::sync_with_stdio(false);
    cin>>n;
    for(int i=1;i<=n;i++)
        cin>>a[i];
    int l=-1,r=5e12+1;
    sort(a+1,a+1+n);

    cout<<check()<<endl;
    return 0;
}
试题 J: 魔法阵
时间限制: 1.0s 内存限制: 512.0MB 本题总分:25 分
【问题描述】
魔法师小蓝为了营救自己的朋友小 Q,来到了敌人布置的魔法阵。魔法阵
可以看作是一幅具有 N 个结点 M 条边的无向图,结点编号为 0, 1, 2, . . . , N − 1,
图中没有重边和自环。敌人在每条边上都布置了陷阱,每条边都有一个伤害属
性 w,每当小蓝经过一条边时就会受到这条边对应的 w 的伤害。小蓝从结点 0
出发,沿着边行走,想要到达结点 N − 1 营救小 Q。
小蓝有一种特殊的魔法可以使用,假设一条路径按照顺序依次经过了以下
L 条边:e1, e2, . . . , eL(可以出现重复的边),那么期间小蓝受到的总伤害就是
P =
∑L
i=1 w(ei),w(ei) 表示边 ei 的伤害属性。如果 L ≥ K,那么小蓝就可以从
这 L 条边当中选出连续出现的 K 条边 ec
, ec+1, . . . , ec+K−1 并免去在这 K 条边行
走期间所受到的伤害,即使用魔法之后路径总伤害变为 P
′ = P −
∑c+K−1
i=c w(ei)。
注意必须恰好选出连续出现的 K 条边,所以当 L < K 时无法使用魔法。
小蓝最多只可以使用一次上述的魔法,请问从结点 0 出发到结点 N − 1 受
到的最小伤害是多少?题目保证至少存在一条从结点 0 到 N − 1 的路径。
【输入格式】
第一行输入三个整数,N, K, M,用空格分隔。
接下来 M 行,每行包含三个整数 u, v,w,表示结点 u 与结点 v 之间存在一
条伤害属性为 w 的无向边。
【输出格式】
输出一行,包含一个整数,表示小蓝从结点 0 到结点 N − 1 受到的最小伤
害。
【样例输入 1】
4 2 3
试题J: 魔法阵 20
第十四届蓝桥杯大赛软件赛省赛 Java 大学 B 组
0 1 2
1 2 1
2 3 4
【样例输出 1】
2
【样例输入 2】
2 5 1
0 1 1
【样例输出 2】
0
【样例说明】
样例 1,存在路径:0 → 1 → 2 → 3,K = 2,如果在 0 → 1 → 2 上使用魔
法,那么答案就是 0 + 0 + 4 = 4;如果在 1 → 2 → 3 上使用魔法,那么答案就
是 2 + 0 + 0 = 2。再也找不到比 2 还小的答案了,所以答案就是 2。
样例 2,存在路径:0 → 1 → 0 → 1 → 0 → 1,K = 5,这条路径总计恰好
走了 5 条边,所以正好可以用魔法消除所有伤害,答案是 0。
【评测用例规模与约定】
对于 30% 的评测用例,1 ≤ N ≤ 20。
对于 50% 的评测用例,1 ≤ N ≤ 100。
对于 100% 的评测用例,1 ≤ N ≤ 1000, 1 ≤ M ≤
N×(N−1)
2 ,1 ≤ K ≤ 10,
0 ≤ u, v ≤ N − 1,1 ≤ w ≤ 1000

思路:最短路+线性dp

压轴题难度并不大很经典的题目,以往最短路中dist[i]表示第i个点中最短的距离

这里就需要稍微转化一下dist[i][j]表示的是第i个点中免费了j条边的费用的最短距离。

所以有三种更新方法

假设此时第i个点的邻接点是j

第一种更新方法就是当dist[i][0]的时候就是正常最短路

也即是dist[j][0]=dist[i][0]+w[i]

第二种更新方式

免费了k条边的最小费用

也就是dist[j][k]=dist[i][k-1]

第三种免费完k条边后的最小费用

dist[j][k]=dist[i][j]+w[i]

结果即使min(dist[n-1][0],dist[n-1][k])

代码

#include<bits/stdc++.h>
using namespace std;
const int N=2100000,M=2100;

int h[N],ne[N],e[N],w[N],idx;
int f[M][M];
bool st[N];
int n,m,k;

void add(int a,int b,int c)
{
    w[idx]=c,e[idx]=b,ne[idx]=h[a],h[a]=idx++;
}

void spfa()
{
    memset(f,0x3f,sizeof f);
    queue<int>q;
    q.push(0);
    f[0][0]=0;
    while(q.size())
    {
        int t=q.front();
        q.pop();

        for(int i=h[t];i!=-1;i=ne[i])
        {
            int j=e[i];
            bool flag=false;
            if(f[j][0]>f[t][0]+w[i])
            {
                f[j][0]=f[t][0]+w[i];
                flag=true;
            }

            for(int l=1;l<=k;l++)
            {
                if(f[j][l]>f[t][l-1])
                {
                    f[j][l]=f[t][l-1];
                    flag=true;
                }
            }

            if(f[j][k]>f[t][k]+w[i])
            {
                f[j][k]=f[t][k]+w[i];
                flag=true;
            }
            if(flag)q.push(j);
        }
    }
}

int main()
{
    ios::sync_with_stdio(false);
    memset(h,-1,sizeof h);
    cin>>n>>k>>m;
    while(m--)
    {
        int a,b,c;
        cin>>a>>b>>c;
        add(a,b,c),add(b,a,c);
    }

    spfa();
    cout<<min(f[n-1][0],f[n-1][k]);
    return 0;
}

 

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值