一. 题目
二.思路整理
对于这道题,我们可以根据示例进行分析:
最后的结果无非只是魏蜀吴三国中的一个,但是如何得到最后的结果呢?
最后结果满足的条件:
- 其中一个国家的士兵数量大于其他两个国家相加
- 回合数要最多
设魏蜀吴第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.什么时候使用贪心算法?
- 所求问题的整体最优解可以通过一系列局部最优的选择来达到
- 当一个问题的最优解包含其子问题的最优解时,称此问题具有最优子结构性
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;
}