龙虎斗

题目描述

轩轩和凯凯正在玩一款叫《龙虎斗》的游戏,游戏的棋盘是一条线段,线段上有 nn 个兵营(自左至右编号 1 ~ nn),相邻编号的兵营之间相隔 1 厘米,即棋盘为长度为 nn − 1 厘米的线段。ii 号兵营里有 cici​ 位工兵。

下图为 nn = 6 的输入描述

轩轩在左侧,代表"龙";凯凯在右侧,代表"虎"。 他们以 mm 号兵营作为分界,靠左的工兵属于龙势力,靠右的工兵属于虎势力,而第 mm 号兵营中的工兵很纠结,他 们不属于任何一方。

一个兵营的气势为:该兵营中的工兵数 ×× 该兵营到 mm 号兵营的距离;参与游戏一方的势力定义为:属于这一方所有兵营的气势之和。 如下图为 nn = 6, mm = 4 的输入描述,其中红色为龙方,黄色为虎方:

游戏过程中,某一刻天降神兵,共有 s1s1​ 位工兵突然出现在了 p1p1​ 号兵营。作为轩轩和凯凯的朋友,你知道如果龙虎双方气势差距太悬殊,轩轩和凯凯就不愿意继续玩下去了。为了让游戏继续,你需要选择一个兵营 p2p2​,并将你手里的 s2s2​ 位工兵全部派往 兵营 p2p2​,使得双方气势差距尽可能小。

注意:你手中的工兵落在哪个兵营,就和该兵营中其他工兵有相同的势力归属(如果落在 mm 号兵营,则不属于任何势力)。

输入描述

第一行包含一个正整数 nn,代表兵营的数量。

接下来的一行包含 nn 个正整数,相邻两数之间以一个空格分隔,第 ii 个正整数代表编号为 ii 的兵营中起始时的工兵数量 cici​。

接下来的一行包含四个正整数,相邻两数间以一个空格分隔,分别代表 m,p1,s1,s2m,p1​,s1​,s2​。

其中,n≤105,ci,s1,s2≤109n≤105,ci​,s1​,s2​≤109。

输出描述

输出一行,包含一个正整数,即 p2p2​,表示你选择的兵营编号。如果存在多个编号同时满足最优,取最小的编号。

输入输出样例

示例

输入

6
2 3 2 3 2 3
4 6 5 2

输出

2

运行限制

  • 最大运行时间:1s
  • 最大运行内存: 256M

总通过次数: 316  |  总提交次数: 486  |  通过率: 65%

难度: 困难   标签: 2018, 模拟, 暴力, 枚举, NOIP

算法思路

本问题需要计算在添加天降神兵后,如何放置手中的工兵使龙虎双方气势差最小。核心思路是:

  1. ​预处理​​:将天降神兵加入指定兵营
  2. ​计算初始气势​​:遍历所有兵营,累加龙方(i < m)和虎方(i > m)的气势
  3. ​枚举最优位置​​:尝试将 s2 工兵放入每个兵营,计算新气势差,记录最小差值和对应位置
  4. ​处理平局​​:若多个位置气势差相同,取最小编号

气势计算公式:

  • 龙方气势:∑(c[i] × (m - i))i < m
  • 虎方气势:∑(c[i] × (i - m))i > m

C++代码实现

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

int main() {
    int n;
    cin >> n;
    long long c[n + 1];  // 兵营工兵数(1-indexed)
    for (int i = 1; i <= n; i++) {
        cin >> c[i];
    }
    
    long long m, p1, s1, s2;
    cin >> m >> p1 >> s1 >> s2;
    c[p1] += s1;  // 天降神兵加入
    
    long long dragon = 0, tiger = 0;
    // 计算初始龙虎气势
    for (int i = 1; i < m; i++) {
        dragon += c[i] * (m - i);
    }
    for (int i = m + 1; i <= n; i++) {
        tiger += c[i] * (i - m);
    }
    
    long long min_diff = abs(dragon - tiger);  // 初始气势差
    long long best_p = m;  // 默认放在分界点
    
    // 枚举所有兵营作为p2
    for (int i = 1; i <= n; i++) {
        long long new_dragon = dragon;
        long long new_tiger = tiger;
        
        if (i < m) {
            new_dragon += s2 * (m - i);  // 龙方增加气势
        } else if (i > m) {
            new_tiger += s2 * (i - m);  // 虎方增加气势
        }
        
        long long diff = abs(new_dragon - new_tiger);
        // 更新最小气势差和最优位置
        if (diff < min_diff || (diff == min_diff && i < best_p)) {
            min_diff = diff;
            best_p = i;
        }
    }
    
    cout << best_p << endl;
    return 0;
}

算法过程演示

以样例1 n=6, c=[2,3,2,3,2,3], m=4, p1=6, s1=5, s2=2 为例:

  1. ​天降神兵​​:c[6] = 3+5 = 8
  2. ​初始气势​​:
    • 龙方:2*(4-1) + 3*(4-2) + 2*(4-3) = 6+6+2 = 14
    • 虎方:2*(5-4) + 8*(6-4) = 2+16 = 18
    • 气势差:|14-18| = 4
  3. ​枚举放置​​:
    p2 新龙方 新虎方 气势差 是否更新
    1 14+6=20 18 2 ✓ (最小)
    2 14+4=18 18 0 ✓ (更小)
    3 14+2=16 18 2
    4 14 18 4
    5 14 18+2=20 6
    6 14 18+4=22 8
  4. ​结果​​:最优位置 p2=2

测试点分析

  1. ​基础样例​​:题目提供样例1和2
  2. ​边界情况​​:
    • n=2(最小规模)
    • m=2 或 m=n-1(分界点靠边)
    • s2=0(无需放置)
  3. ​特殊场景​​:
    • 天降神兵落在 m(不影响气势)
    • 初始气势相等(放置 m 最优)
    • 多个位置产生相同最小差(取最小编号)
  4. ​极端数据​​:
    • n=10^5(需 O(n) 算法)
    • c[i]=s1=s2=10^9(需 long long

优化建议

  1. ​避免重复计算​​:增量更新气势(已实现)
  2. ​提前终止​​:若气势差为0可提前结束(但需考虑平局)
  3. ​二分优化​​:对气势函数单调性分析(复杂且不必要)
  4. ​内存优化​​:用 vector 动态数组(n 较大时)

注意事项

  1. ​数据类型​​:必须用 long long 避免溢出(气势值最大 10^14 级)
  2. ​下标处理​​:兵营编号从1开始,数组需开 n+1
  3. ​分界点逻辑​​:m 号兵营不参与气势计算
  4. ​天降神兵​​:先更新 c[p1] 再计算气势
  5. ​平局处理​​:当多个位置气势差相同时,取最小编号
Logo

集算法之大成!助力oier实现梦想!

更多推荐