牛客周赛 Round 40题解

 题解视频:牛客周赛round40,质量很高的一场周赛!背包不熟练同学的一定要补题!!!_哔哩哔哩_bilibili

题号标题已通过代码通过率我的状态
A小红进地下城点击查看1419/1624未通过
B小红打怪点击查看1266/2342未通过
C小红的排列构造点击查看851/2591未通过
D小红升装备点击查看387/2283未通过
E小红的矩阵划分点击查看132/3143未通过
F小红拿宝箱点击查看27/1097未通过

C.小红的排列构造

#include<bits/stdc++.h>
using namespace std;
const int N = 202020;
int a[N],p[N],q[N];
int t1[N],t2[N];
int main(){
    int n,i,j=1,j2=1;
    int jud = 0;
    cin>>n;
    for(i=1;i<=n;i++){
        cin>>a[i];
        if(!t1[a[i]]) p[i] = a[i],t1[a[i]] = 1;
        else if(!t2[a[i]]) q[i]=a[i],t2[a[i]] = 1;
        else jud = 1;
    }
    if(jud) return cout<<-1,0;
    for(i=1;i<=n;i++){
        if(!p[i]){
            while(t1[j]) j++;
            p[i] = j;
            t1[j] = 1;
        }
        if(!q[i]){
            while(t2[j2]) j2++;
            q[i] = j2;
            t2[j2] = 1;
        }
    }
    for(i=1;i<=n;i++) cout<<p[i]<<" ";
    cout<<endl;
    for(i=1;i<=n;i++) cout<<q[i]<<" ";
    cout<<endl;
    return 0;
}


模拟过程:

让我们通过一个具体的例子来解释上述代码的运行过程。假设输入的数组 \( n \) 的长度为 5,并且数组 \( a \) 的值为 [3, 1, 2, 1, 3]。我们将逐步追踪代码如何操作这个输入并生成输出的两个排列 \( p \) 和 \( q \)。

### 输入过程

- **输入 n = 5**。
- **输入 a = [3, 1, 2, 1, 3]**。

### 初始化排列 \( p \) 和 \( q \)

首先,代码将遍历数组 \( a \),尝试将每个元素尽可能地放入排列 \( p \) 和 \( q \) 中。

- **i = 1**: \( a[1] = 3 \)。因为 \( t1[3] \) 是0,所以 \( p[1] = 3 \),并将 \( t1[3] \) 设为 1。
- **i = 2**: \( a[2] = 1 \)。因为 \( t1[1] \) 是0,所以 \( p[2] = 1 \),并将 \( t1[1] \) 设为 1。
- **i = 3**: \( a[3] = 2 \)。因为 \( t1[2] \) 是0,所以 \( p[3] = 2 \),并将 \( t1[2] \) 设为 1。
- **i = 4**: \( a[4] = 1 \)。因为 \( t1[1] \) 已经是1(\( p[2] = 1 \)),所以 \( q[4] = 1 \),并将 \( t2[1] \) 设为 1。
- **i = 5**: \( a[5] = 3 \)。因为 \( t1[3] \) 已经是1(\( p[1] = 3 \)),所以 \( q[5] = 3 \),并将 \( t2[3] \) 设为 1。

此时,数组 \( p \) 和 \( q \) 的状态为:
- **\( p = [3, 1, 2, 0, 0] \)**
- **\( q = [0, 0, 0, 1, 3] \)**

### 填充未分配的位置

- **i = 1**: \( p[1] \) 和 \( q[1] \) 已分配,跳过。
- **i = 2**: \( p[2] \) 和 \( q[2] \) 已分配,跳过。
- **i = 3**: \( p[3] \) 和 \( q[3] \) 已分配,跳过。
- **i = 4**: \( p[4] \) 未分配:
  - 找到 \( j = 4 \) 未被使用,所以 \( p[4] = 4 \),并将 \( t1[4] \) 设为 1。
- \( q[4] \) 已分配,跳过。
- **i = 5**: \( p[5] \) 未分配:
  - 找到 \( j = 5 \) 未被使用,所以 \( p[5] = 5 \),并将 \( t1[5] \) 设为 1。
- \( q[5] \) 已分配,跳过。
- 对于 \( q \) 的空位:
  - \( q[1] \),找到 \( j2 = 2 \) 未被使用,所以 \( q[1] = 2 \),并将 \( t2[2] \) 设为 1。
  - \( q[2] \),找到 \( j2 = 4 \) 未被使用,所以 \( q[2] = 4 \),并将 \( t2[4] \) 设为 1。
  - \( q[3] \),找到 \( j2 = 5 \) 未被使用,所以 \( q[3] = 5 \),并将 \( t2[5] \) 设为 1。

### 输出结果

最终排列:
- **\( p = [3, 1, 2, 4, 5] \)**
- **\( q = [2, 4, 5, 1, 3] \)**

代码成功生成了两个有效的排列,每个数字在两个排列中各出现一次。

D.小红升装备

思路:多重背包dp。

#include<bits/stdc++.h>
using namespace std;
long long dp[303][303];
int main(){
    int n,i,j,x,k;
    cin>>n>>x;
    memset(dp,-0x3f,sizeof dp);
    dp[0][0] = 0;
    long long res = 0;
    for(i=1;i<=n;i++){
        long long a,b,c,d,e;
        cin>>a>>b>>c>>d>>e;
        for(j=0;j<=x;j++) dp[i][j] = dp[i-1][j];
        for(k=0;k<=x;k++){
            for(j=0;j<=e;j++){
                if(b+c*j>k){
                    break;
                }
                dp[i][k] = max(dp[i][k],dp[i-1][k-(b+c*j)]+a+j*d);
                res = max(res,dp[i][k]);
            }
        }
    }
    cout<<res<<endl;
    return 0;
}

这段代码是用于解决上述问题的动态规划解决方案。这里的目标是最大化小红通过购买和升级装备获得的战力,同时不超过她拥有的金币数。以下是解题思路和代码解释:

动态规划定义

  • dp[i][j] 表示考虑前 i 件装备,使用不超过 j 个金币时能获得的最大战力。
  • 初始条件为 dp[0][0] = 0,意味着没有装备和没有使用金币时战力为 0。
  • 初始化其它值为一个很小的数(这里用 -0x3f3f3f3f),代表非法状态。

状态转移

对于每件装备,遍历所有可能的金币使用量 k(0 到 x),并考虑将该装备升级 j 级(0 到最大等级 e)。状态转移遵循以下逻辑:

  1. 若购买该装备并升级 j 级所需的总金币数超过 k,则不可能实现这种情况,结束当前循环。
  2. 更新 dp[i][k]max(dp[i][k], dp[i-1][k-(b+c*j)] + a + j*d)。这意味着,如果决定购买并升级这个装备 j 级,将会消耗 b+c*j 金币,且增加的战力为初始战力 a 加上 j*d(每级升级的战力)。
  3. 更新最大战力 res 为遍历过程中所有 dp[i][k] 的最大值。

循环结构

  • 外层循环遍历装备 i 从 1 到 n
  • 第二层循环遍历金币数 k 从 0 到 x,表示不同的预算情况。
  • 内层循环尝试对当前装备进行从 0 到 e 级的升级,并检查是否可能在当前金币预算下完成购买和升级。

输出结果

最终,输出 res,它表示在给定预算下,通过购买和升级装备可以达到的最大战力。

通过这种方式,代码能够在多重限制下(金币数和装备升级限制)找到最优解。这种问题常见于资源优化和游戏策略开发中,动态规划提供了一种有效的解决方案。

E.小红的矩阵划分

思路:一道思维题,考虑三种情况,一种是全填L,一种是全填2*2,一种是去掉一个L填2*2。

#include<bits/stdc++.h>
using namespace std;
int main(){
    long long n,x,y;
    cin>>n>>x>>y;
    if(n%3==0){
        cout<<max(n*n/3*x,n*n/4*y);
    }else{
        cout<<max({n*n/3*x,n*n/3*x-x+y,n*n/4*y});
    }
    return 0;
}

算法思路

  1. 计算 n*n/3n*n/4,这代表如果整个矩阵被 'L' 型或 2*2 连通块完全覆盖,各自能够划分出的最大连通块数。
  2. 对于每一种情况,计算对应的总分,即 n*n/3*xn*n/4*y
  3. 如果 n 不能被 3 整除,则额外计算一个 'L' 型和一个 22 连通块混合的情况,即 n*n/3*x-x+y,这种情况下会牺牲一个 'L' 型连通块和一个格子来放置一个 22 连通块。
  4. 在所有这些情况中取最大值作为最终得分。

F.小红拿宝箱

太难了,看不懂,跳了。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值