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

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/haishu_zheng/article/details/87098515

第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;
}

评测结果:

2.png

第二种方法,用龙势力和虎势力的气势差除去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;
}

少儿编程咨询、算法咨询请加微信307591841或QQ群581357582
诺依曼算法公众号.jpg

猜你喜欢

转载自blog.csdn.net/haishu_zheng/article/details/87098515