NOIP 2018普及组复赛C++详细题解报告(2)

时间:2022-06-18
本文章向大家介绍NOIP 2018普及组复赛C++详细题解报告(2),主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

第2题 龙虎斗

一、分析

(1)ci最大值是10亿,n最大值是10万,相乘明显会超过INT_MAX,所以本题要用long long才有可能得满分。若用int,最多只能得80分。 (2)若能想到c是count的缩写,p是position的缩写,s是soldier的缩写,对理解题意会有所帮助。 (3)p1和s1是个很弱的干扰项,直接加到该位置即可。

二、算法实现

求气势差的最小值,有两种方法。 第一种方法是暴力枚举,每移动一次,就判断气势差距是不是变得更小了。这种方法,最多可能要计算10万次。通常几百万内的循环不会运行超时。

方法一 完整代码

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;

#define LL long long

int main(){
    freopen("fight.in","r",stdin);    
    freopen("fight.out","w",stdout);    
    int n, i, m, p1, p2;    
    cin >> n;    
    LL c[n + 1], s1, s2;    
    for(i = 1; i <= n; i++)    
    {
        cin >> c[i];    
    }    
    cin >> m >> p1 >> s1 >> s2;    
    c[p1] += s1;  
      
    LL lPower = 0; // 龙势力    
    for(i = 1; i < m; i++)    
    {    
        lPower += c[i] * (m - i);    
    }  
      
    LL rPower = 0;    
    for(i = m + 1; i <= n; i++)    
    {    
        rPower += c[i] * (i - m);    
    }    
    
    //cout << lPower << ' ' << rPower << endl;   
     
    LL gap = abs(lPower - rPower); // 双方的气势差    
    p2 = m; //默认放在m位置    
    for(i = 1; i <= n; i++)    
    {    
        if(i < m)   // 神兵加到左侧龙势力        
        {      
             if(abs(lPower + s2 * (m - i) - rPower) < gap)            
             {         
                 gap = abs(lPower + s2 * (m - i) - rPower);                
                 p2 = i;            
              }        
         }        
         else if(i > m) // 神兵加到右侧虎势力        
         {        
             if(abs(rPower + (i - m) * s2 - lPower) < gap)            
             {           
                 gap = abs(rPower + (i - m) * s2 - lPower);                
                 p2 = i;            
             }        
         }    
     }    
     cout << p2 << endl;    
     return 0;
}

评测结果:

第二种方法,用龙势力和虎势力的气势差除去s2,会得到s2离m号兵营的距离dist。注意dist不能定义为整数,只能定义为浮点数。当龙势力小于虎势力且距离中包含0.5时,要往左多挪一个位置,这样得到的p2与m的距离最大,从而p2取得最小值 ;当龙势力大于虎势力且距离中包含0.5时,0.5直接去掉,这样就不用往右再挪一个距离了。这样得到的p2与m的距离最小,从而p2取得最小值。 举两个例子,这两个例子里不用考虑p1和s1,对理解题意影响不大。 例1 n = 3,c1 = 10, c3 = 11,c2等于随便一个数,比如1。m = 2。s2 = 2。 在s2没放进去时,势力差gap = 虎势力 - 龙势力 = 1。dist = gap / s2 = 0.5,所以p2 = m – int(dist + 0.5) = 1,即要把s2放到1号营里。 此时龙势力 = 10 + 2 = 12, 虎势力 = 11,gap仍然是1,但此时的p2最小。

例2 n = 3,c1 = 11, c3 = 10,c2等于随便一个数,比如1。m = 2。s2 = 2。 在s2没放进去时,势力差gap = 龙势力 – 虎势力 = 1。dist = gap / s2 = 0.5,所以p2 = m + ceil(dist -0.5) = m + 0 = m,即要把s2放到中立的m号营里。 此时龙势力 = 11, 虎势力 = 10,m号营势力 = 1 + 2 = 3,gap仍然是1,此时的p2最小。

方法二完整代码

#include <iostream>
#include <cmath>
#include<cstdio>
using namespace std;
#define LL long long
int main()
{
    freopen("fight21.in","r",stdin);    
    freopen("fight.out","w",stdout);    
    int n, i, m, p1, p2;    
    LL s1, s2;    
    cin >> n;    
    LL c[n + 1];    
    for(i = 1; i <= n; i++)    
    {    
        cin >> c[i];    
    }    
    cin >> m >> p1 >> s1 >> s2;    
    c[p1] += s1;    
    LL lPower = 0; // 龙势力    
    for(i = 1; i < m; i++)    
    {    
        lPower += c[i] * (m - i);    
    }    
    LL rPower = 0;    
    for(i = m + 1; i <= n; i++)    
    {    
        rPower += c[i] * (i - m);    
    }    
    //cout << lPower << ' ' << rPower << endl;    
    LL gap = abs(lPower - rPower); // 双方的气势差    
    p2 = m;          // 暂放在m的位置上    
    float dist = gap * 1.0 / s2; // s2离m兵营的距离    
    if(rPower > lPower)  // 虎势力大,s2要放在龙方    
    {        
        //这里floor()也可以改为int()        
        p2 = m - floor(dist + 0.5);        
        if(p2 < 1)        
        {        
            p2 = 1; // 不能越界        
        }    
    }    
    else if(rPower < lPower)    
    {        
        // 注意,这里0.5是要舍弃掉的,大于0.5才加1        
        p2 = m + ceil(dist - 0.5);        
        if(p2 > n)        
        {        
            p2 = n; // 不能越界        
        }    
    }    
    cout << p2 << endl;    
    return 0;
}