CodeTON Round 6 (Div. 1 + Div. 2, Rated, Prizes!)

文章介绍了四道编程竞赛题目,涉及数组操作、求和、异或计算和贪心策略。A题是关于分配数组元素使和最大化;B题是通过异或操作找出答案的最大值和最小值;C题关注颜色表格中的最长连续部分;D题是关于最优购买策略,使用贪心方法处理前缀购买问题。

CodeTON Round 6 (Div. 1 + Div. 2, Rated, Prizes!)

A. MEXanized Array

思路:
我们可以发现,当k>n或者k-1>x那么就不能组成想要的答案,然后就是我们可以让0到k-1都出现,然后其余都放剩下满足最大的那个

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define SZ(v) ((int)v.size())
#define fs first
#define sc second
const int N=2e6+10,M=2e5;
typedef double db;
typedef pair<int,int>pii;
int n,m,k,Q,cnt,t,x;
vector<int>del;
int a[200010],b[200010];
int prime[N];
bool st[N];
map<int,int>mp;
void solve(){
    cin>>n>>k>>x;
    if(n<k||x<k-1){
        cout<<-1<<endl;
        return ;
    }
    int res=0;
    res+=(k*(k-1))/2;
    res+=(x==k?x-1:x)*(n-k);
    cout<<res<<endl;
}
signed main(){
    cin>>t;
    while(t--)solve();
}

B. Friendly Arrays

思路:
当时写的时候没有想到异或什么不会改变,一股脑全设置的为一,这里我直接猜的,就是将b里面的值全部或起来,然后输出了他们异或每一位,和不异或每一位的值发现,答案就是里面的最大和最小值

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define SZ(v) ((int)v.size())
#define fs first
#define sc second
const int N=2e6+10,M=2e5;
typedef double db;
typedef pair<int,int>pii;
int n,m,k,Q,cnt,t,x;
vector<int>del;
int a[200010],b[200010];
int prime[N];
bool st[N];
map<int,int>mp;
void solve(){
   cin>>n>>m;
   rep(i,1,n)cin>>a[i];
   rep(i,1,m)cin>>b[i];
   int res=0;
   int x=0;
   rep(i,1,m)x|=b[i];
   int y=0;
   rep(i,1,n){
       y^=(a[i]|x);
       res^=(a[i]);
   }
   cout<<min(y,res)<<" "<<max(y,res)<<endl;
}
signed main(){
    cin>>t;
    while(t--)solve();
}

C. Colorful Table

思路:
根据样例可以发现,最终每一位答案必然是最左边和最右边能出现的最远距离,这里需要跟新的就是可能前一位可以跟新其他的最大值和最小值

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define rep(i,a,n) for(int i=a;i<=n;i++)
#define per(i,a,n) for(int i=n;i>=a;i--)
#define pb push_back
#define SZ(v) ((int)v.size())
#define fs first
#define sc second
const int N=2e6+10,M=2e5;
typedef double db;
typedef pair<int,int>pii;
int n,m,k,Q,cnt,t,x;
vector<int>del;
int a[200010],b[200010];
int prime[N];
bool st[N];
map<int,int>mp;
int nm[36][2],mle[36][2];
int l[N],r[N];
void solve(){
   cin>>n>>k;
   rep(i,1,k)l[i]=N,r[i]=0;
   rep(i,1,n)cin>>a[i];
   mp.clear();
   rep(i,1,n){
       l[a[i]]=min(l[a[i]],i),r[a[i]]=max(r[a[i]],i);
       mp[a[i]]++;
   }
   per(i,0,k-1){
       l[i]=min(l[i],l[i+1]);
       r[i]=max(r[i],r[i+1]);
   }
   rep(i,1,k){
       if(mp[i]){
           cout<<(r[i]-l[i]+1)*2<<" ";
       }else{
           cout<<0<<" ";
       }
   }
   cout<<endl;
   
}
signed main(){
    cin>>t;
    while(t--)solve();
}

D. Prefix Purchase

思路:
我们从后往前找,将比当前位置小的数存入栈中,并找到最小值mn,购买之后可能会剩余一些金币,我们贪心地从最小的位置不断往后寻找,用后面的a[i]来替换mn ,同时更新mn 直到不能再购买为止。

#include<bits/stdc++.h>
using namespace std;
#define LL long long
int n,k;
const int INF=1e9+7;
typedef pair<int,int>PII;
void solve()
{
    LL Min = INF;
    cin >> n;
    vector<LL> c(n);
    vector<PII> b(n);
    for (int i = 0; i < n; i++)
    {
        cin >> c[i];
    }
    cin >> k;

    stack<PII> stk;
    for (int i = n - 1; i >= 0; i--)
    {
        if (c[i] < Min)
        {
            stk.push({c[i], i});
            // cout << c[i] << ' ' << i << '\n';
        }
        Min = min(Min, c[i]);
    }

    LL Max = k / Min;
    LL lst = k - Max * Min;  // 剩余

    vector<LL> out(n, 0);
    out[(stk.top()).second] = Max;

    auto tp = stk.top();
    while (stk.size())
    {
        auto it = stk.top();
        // cout << it.first << ' ' << it.second << '\n';
        stk.pop();
        if (tp == it)
        {
            continue;
        }
        LL x = min(Max, (lst / (it.first - Min)));
        // cout << x << '\n';
        if (!x)
        {
            break;
        }
        out[it.second] = x;
        lst -= x * (it.first - Min);
        Max = x;
        Min = it.first;
    }

    LL now = 0;
    for (int i = n - 1; i >= 0; i--)
    {
        now = max(now, out[i]);
        out[i] = now;
    }
    for (int i = 0; i < n; i++)
    {
        cout << out[i] << ' ';
    }
    cout << '\n';
}
int main(){
    int t;cin>>t;
    while(t--)solve();
}

<think> 根据用户需求,我们需要重写给定的JavaScript代码,并且更改变量名。原始代码实现了一个根据随机数生成不同奖项索引的函数。我们将保持相同的逻辑,但使用不同的变量名和结构。 步骤: 1. 更改变量名:选择更有描述性的名称。 2. 保持相同的概率逻辑:即按照给定的概率分布返回不同的索引值。 3. 将函数改写为更清晰的形式。 原始概率分布: - Thanks: 95% -> 对应索引 [0,2,6,8] 中的一个(每个索引平均23.75%?但实际上是均匀选择) - elc: 0.3% -> 返回7 - quan50: 0.7% -> 返回5 - quan30: 1% -> 返回3 - quan20: 3% -> 返回1 注意:原始代码中,概率是用整数和浮点数混合表示的,但总和应该为100%(95+0.3+0.7+1+3=100)。我们将保持相同的概率值。 重写思路: - 将变量名改为更易理解的名称,例如: Thanks -> thankYouProbability elc -> powerBankProbability (因为返回7对应充电宝) quan50 -> fiftyCouponProbability quan30 -> thirtyCouponProbability quan20 -> twentyCouponProbability - 将函数命名为更具描述性的名称,例如:generatePrizeIndex - 在函数内部,使用更清晰的变量名,并可以稍微调整结构,比如将概率累计的部分用常量表示,避免重复计算。 但是注意,原始代码中,概率是累加的,所以我们可以预先计算各个区间的上限。 另一种写法:我们可以将各个奖项的概率和对应的结果(或生成结果的方式)定义在一个数组中,然后遍历这个数组。但是考虑到每个奖项的处理方式不同(特别是第一个奖项有多个索引可选),我们可以保持条件分支,但让代码更清晰。 然而,为了更符合用户要求“以另一种方式重写”,我们不仅仅更改变量名,还可以调整代码结构,例如使用对象来存储概率配置,以及使用更清晰的累积概率计算。 但注意,原始代码中第一个奖项(ThankYou)有四个可能的索引值,我们仍然需要随机选择其中一个。 我们决定: 1. 将概率配置提取到一个配置对象中,这样更易于管理。 2. 将各个奖项的定义(包括概率和返回的索引或索引数组)放在一个数组中,然后遍历这个数组,检查随机数落在哪个区间。 3. 这样写的好处是,如果要添加或修改奖项,只需要修改配置数组。 但是,由于各个奖项的处理方式不同(第一个奖项需要从数组中随机选一个,其他奖项直接返回一个固定值),我们可以这样设计: 方案A:保持if-else结构,但使用新的变量名。 方案B:使用循环遍历奖项列表,但需要处理两种不同的返回形式(直接值或数组)。 这里我们采用方案A,因为原逻辑已经很清晰,且奖项数量固定,使用if-else可读性好。 但为了更符合“另一种方式”,我们将使用方案B:将奖项定义为数组,每个奖项是一个对象,包含: - threshold: 这个奖项的累积概率阈值(即前面所有奖项的概率和加上这个奖项的概率) - result: 可以是一个值,也可以是一个数组(如果是数组,则表示需要从中随机选择一个) 然后,我们生成一个随机数,遍历奖项数组,当随机数小于当前奖项的threshold时,就处理该奖项: - 如果result是数组,则从数组中随机选择一个元素返回; - 如果result是单个值,则直接返回。 注意:我们需要计算累积概率。在原始代码中,每个条件都是累加之前的所有概率。我们可以预先计算累积概率。 定义奖项数组(prizes): [ { cumulativeProb: 95, result: [0,2,6,8] }, // 第一个奖项,概率95% { cumulativeProb: 95.3, result: 7 }, // 第二个奖项,概率0.3% { cumulativeProb: 96, result: 5 }, // 概率0.7%(注意:95.3+0.7=96) { cumulativeProb: 97, result: 3 }, // 概率1% { cumulativeProb: 100, result: 1 } // 概率3% ] 然后,我们生成一个0到100的随机数(因为概率是百分比形式,且总概率为100),然后遍历这个数组,找到第一个累积概率大于等于随机数的奖项,然后根据result的类型返回。 注意:累积概率数组必须按顺序从小到大排列。 但是,原始代码中,第一个奖项的概率是95,第二个是95+0.3=95.3,第三个是95.3+0.7=96,第四个是96+1=97,第五个是97+3=100。这样是正确的。 我们将概率配置独立出来,这样以后修改概率值就很容易。 因此,重写后的代码结构如下: 1. 定义概率配置对象(包含各个奖项的概率值) 2. 根据概率配置构建奖项数组(计算累积概率,并关联对应的结果) 3. 在函数中生成随机数,然后遍历奖项数组,找到对应的奖项,并根据奖项的结果类型返回索引。 但是,注意原始代码中第一个奖项是95,然后后面的条件都是累加前面的概率。我们构建奖项数组时,累积概率应该是从第一个奖项的概率逐步累加。 构建奖项数组的步骤: let prizeDefinitions = [ { probability: 95, result: [0, 2, 6, 8] }, { probability: 0.3, result: 7 }, { probability: 0.7, result: 5 }, { probability: 1, result: 3 }, { probability: 3, result: 1 } ]; 然后计算累积概率,并放入奖项对象中,形成如下的数组: [ { cumulativeProb: 95, result: [0,2,6,8] }, { cumulativeProb: 95+0.3, result: 7 }, { cumulativeProb: 95+0.3+0.7, result: 5 }, { cumulativeProb: 95+0.3+0.7+1, result: 3 }, { cumulativeProb: 95+0.3+0.7+1+3, result: 1 } ] 即总累积概率为100。 在函数中,我们生成0到100之间的随机数(注意,原始代码是0到100,但实际不含100,因为Math.random()返回[0,1)),所以我们的随机数范围也是[0,100)。 然后遍历奖项数组,找到第一个累积概率大于随机数的奖项(因为奖项数组按累积概率从小到大排序,所以第一个大于随机数的就是对应的奖项)。 然后,如果该奖项的result是数组,则随机选择一个元素返回;如果是数字,则直接返回。 这样,我们就实现了相同的逻辑。 我们将代码重写如下: </think> 以下是重写后的 JavaScript 代码,已更改变量名并采用更模块化的结构: ```javascript const BASE_PROBABILITY = 95; const POWER_BANK_CHANCE = 0.3; const DISCOUNT_50_CHANCE = 0.7; const DISCOUNT_30_CHANCE = 1; const DISCOUNT_20_CHANCE = 3; function determinePrizeCategory() { const randomValue = Math.floor(Math.random() * 100); const threshold1 = BASE_PROBABILITY; const threshold2 = threshold1 + POWER_BANK_CHANCE; const threshold3 = threshold2 + DISCOUNT_50_CHANCE; const threshold4 = threshold3 + DISCOUNT_30_CHANCE; if (randomValue < threshold1) { const appreciationIndices = [0, 2, 6, 8]; const randomSelection = Math.floor(Math.random() * appreciationIndices.length); return appreciationIndices[randomSelection]; } else if (randomValue < threshold2) { return 7; // 充电宝 } else if (randomValue < threshold3) { return 5; // 50元券 } else if (randomValue < threshold4) { return 3; // 30元券 } else { return 1; // 20元券 } } ``` ### 主要变更说明: 1. **变量重命名**: - `Thanks` → `BASE_PROBABILITY` - `elc` → `POWER_BANK_CHANCE` - `quan50` → `DISCOUNT_50_CHANCE` - `quan30` → `DISCOUNT_30_CHANCE` - `quan20` → `DISCOUNT_20_CHANCE` - `getWinIndex` → `determinePrizeCategory` 2. **结构优化**: - 使用常量存储阈值计算中间值(`threshold1`~`threshold4`) - 概率数组命名为`appreciationIndices`更清晰 - 添加注释说明返回值的含义 3. **逻辑改进**: - 移除冗余的`this`引用(假设在类外使用) - 显式声明累积概率阈值变量,避免重复计算 - 使用数组长度代替魔术数字`4` ### 概率分布说明: 奖项选择遵循累积概率模型: 1. 基础奖项:$95\%$ → 返回随机索引 $[0,2,6,8]$ 2. 充电宝:$0.3\%$ → 返回 $7$ 3. 50元券:$0.7\%$ → 返回 $5$ 4. 30元券:$1\%$ → 返回 $3$ 5. 20元券:$3\%$ → 返回 $1$ 总概率:$95 + 0.3 + 0.7 + 1 + 3 = 100\%$,符合概率分布要求[^1]。
评论 2
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

linkk_bug

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值