三国游戏学习笔记——蓝桥杯2023年第十四届省赛真题

一. 题目

.思路整理

对于这道题我们可以根据示例进行分析

最后的结果无非只是魏蜀吴三国中的一个但是如何得到最后的结果呢

最后结果满足的条件

  1. 其中一个国家的士兵数量大于其他两个国家相加
  2. 回合数要最多

魏蜀吴第i回合的士兵数量分别为A[i],B[i],C[i],那么魏国胜利的条件为A[i]-B[i]-C[i]>0蜀国吴国相同三次得到的回合数取最大的那个数即可此时我们D1[i]=A[i]-B[i]-C[i]

这里可以看出得出最后结果分为两个阶段

1.每个获胜的最大回合数

2.三个国家得到的回合数最大值

有没有发现这里局部最优解整体最优解过程

所以我们可以使用贪心算法

.贪心算法

1.什么时候使用贪心算法

  1. 所求问题的整体最优解可以通过一系列局部最优的选择达到
  2. 当一个问题的最优解包含子问题的最优解此问题具有最优子结构性

2.贪心算法的本质

     是大问题不停分解为小问题通常以自上而下的方式进行

3.贪心算法的步骤

    ①建立数学模型来描述问题

    ②求解的问题分成若干个子问题

    ③对每个问题求解得到子问题的局部最优解

    ④子问题的局部最优解合成原来问题的一个解

.思路整理

部分我们整理了大致思路我们根据贪心算法理一下整体思路

我们最后需要得到的是魏蜀吴三国中其中一个如何得到最后的结果呢一是其中一个国家的士兵大于其他两国士兵总数还有一个是得到这个结果的回合数最大那么把这个结果我们拆解为分别求三个国家的最优解然后局部最优解再求整体最优解即可得到最终的结果

三个国家的最优解如何得到呢

以魏国为例,我们使用D[i]数组来记录i回合的A[i]-B[i]-C[i]只要大于零我们就认为魏国但是这未免也太简单了我们要求的是最大回合数所以即使小于零也存在反败为胜的情况此时这种小于零的情况我们也要算到回合数中。即没到最后回合前面的正数对应的最大回合数可能不是部分的最优解

意思是尽可能多还要保证最终的和大于0那么这个意思是是不是指正数尽可能多呢为了得到最优解我们选中的正数越大越好我们才能加上更多的负数从而增大回合数

因此我们将D数组从大到小排序这一步的目的是先对正数进行计算然后再算负数去计算每回合达到最优解贡献度

所以代码如下

.代码

include <bits/stdc++.h>

using namespace std;

typedef long long ll;//不开long long将挂掉两个测试点 

const int N=1e5+100;

bool cmp(int a,int b)//从大到小排序 

{

    return a>b;

}

int A[N],B[N],C[N];//存放魏蜀吴三个国家发生对应事件的得分 

int weight[N];//weight[i]存放魏/蜀/吴发生第i件事对于总得分的影响 

int n;

int get_ans(int a[],int b[],int c[])//a获胜,发生最多的事件数 

{

    ll sum=0;//累加当前总贡献 

    memset(weight,0,sizeof(weight));//weight数组清零 

    for(int i=1;i<=n;i++)//计算每个事件对a的纯贡献 

    {

        //假设a[i]=7,b[i]=2,c[i]=2,则发生i事件对a的纯贡献是7-2-2=3,

        //可以理解为该发生事件使a比b+c的得分高了3 

        weight[i]=a[i]-b[i]-c[i];

    }        

    sort(weight+1,weight+n+1,cmp);//将纯贡献按从大到小排序 

    int ans=0;//记录可以发生多少事件使a>b+c 

    for(int i=1;i<=n;i++)

    {

        sum+=weight[i];//累加贡献 

        if(sum>0)ans++;//累计贡献严格大于0,计数加1 

        else break;//若不大于0,由于序列递减,后面只会越来越小,直接退出 

    }

    if(ans==0)return -1;//一个事件都不能发生,返回-1 

    return ans;

}

int main()

{

    scanf("%d",&n);

    //输入原始数据,A、B、C分别存储魏蜀吴三个国家发生对应事件增加的得分 

    for(int i=1;i<=n;i++)scanf("%d",&A[i]);

    for(int i=1;i<=n;i++)scanf("%d",&B[i]);

    for(int i=1;i<=n;i++)scanf("%d",&C[i]);

    int ans_A=get_ans(A,B,C);//假设魏国获胜,发生最多的事件数 

    int ans_B=get_ans(B,A,C);//假设蜀国获胜,发生最多的事件数 

    int ans_C=get_ans(C,A,B);//假设吴国获胜,发生最多的事件数 

    int ans=max(ans_A,max(ans_B,ans_C));//三者取最大值即可 

    printf("%d\n",ans);

    return 0;

}

  • 27
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值