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

叁歲伎倆 2022-03-20 11:22 289阅读 0赞

第2题 龙虎斗

一、分析

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

二、算法实现

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

方法一 完整代码

  1. #include <iostream>
  2. #include <cstdio>
  3. #include <cmath>
  4. using namespace std;
  5. #define LL long long
  6. int main()
  7. {
  8. freopen("fight.in","r",stdin);
  9. freopen("fight.out","w",stdout);
  10. int n, i, m, p1, p2;
  11. cin >> n;
  12. LL c[n + 1], s1, s2;
  13. for(i = 1; i <= n; i++)
  14. {
  15. cin >> c[i];
  16. }
  17. cin >> m >> p1 >> s1 >> s2;
  18. c[p1] += s1;
  19. LL lPower = 0; // 龙势力
  20. for(i = 1; i < m; i++)
  21. {
  22. lPower += c[i] * (m - i);
  23. }
  24. LL rPower = 0;
  25. for(i = m + 1; i <= n; i++)
  26. {
  27. rPower += c[i] * (i - m);
  28. }
  29. //cout << lPower << ' ' << rPower << endl;
  30. LL gap = abs(lPower - rPower); // 双方的气势差
  31. p2 = m; //默认放在m位置
  32. for(i = 1; i <= n; i++)
  33. {
  34. if(i < m) // 神兵加到左侧龙势力
  35. {
  36. if(abs(lPower + s2 * (m - i) - rPower) < gap)
  37. {
  38. gap = abs(lPower + s2 * (m - i) - rPower);
  39. p2 = i;
  40. }
  41. }
  42. else if(i > m) // 神兵加到右侧虎势力
  43. {
  44. if(abs(rPower + (i - m) * s2 - lPower) < gap)
  45. {
  46. gap = abs(rPower + (i - m) * s2 - lPower);
  47. p2 = i;
  48. }
  49. }
  50. }
  51. cout << p2 << endl;
  52. return 0;
  53. }

评测结果:

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最小。

方法二完整代码

  1. #include <iostream>
  2. #include <cmath>
  3. #include<cstdio>
  4. using namespace std;
  5. #define LL long long
  6. int main()
  7. {
  8. freopen("fight21.in","r",stdin);
  9. freopen("fight.out","w",stdout);
  10. int n, i, m, p1, p2;
  11. LL s1, s2;
  12. cin >> n;
  13. LL c[n + 1];
  14. for(i = 1; i <= n; i++)
  15. {
  16. cin >> c[i];
  17. }
  18. cin >> m >> p1 >> s1 >> s2;
  19. c[p1] += s1;
  20. LL lPower = 0; // 龙势力
  21. for(i = 1; i < m; i++)
  22. {
  23. lPower += c[i] * (m - i);
  24. }
  25. LL rPower = 0;
  26. for(i = m + 1; i <= n; i++)
  27. {
  28. rPower += c[i] * (i - m);
  29. }
  30. //cout << lPower << ' ' << rPower << endl;
  31. LL gap = abs(lPower - rPower); // 双方的气势差
  32. p2 = m; // 暂放在m的位置上
  33. float dist = gap * 1.0 / s2; // s2离m兵营的距离
  34. if(rPower > lPower) // 虎势力大,s2要放在龙方
  35. {
  36. //这里floor()也可以改为int()
  37. p2 = m - floor(dist + 0.5);
  38. if(p2 < 1)
  39. {
  40. p2 = 1; // 不能越界
  41. }
  42. }
  43. else if(rPower < lPower)
  44. {
  45. // 注意,这里0.5是要舍弃掉的,大于0.5才加1
  46. p2 = m + ceil(dist - 0.5);
  47. if(p2 > n)
  48. {
  49. p2 = n; // 不能越界
  50. }
  51. }
  52. cout << p2 << endl;
  53. return 0;
  54. }

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

发表评论

表情:
评论列表 (有 0 条评论,289人围观)

还没有评论,来说两句吧...

相关阅读