洛谷P5016 [NOIP2018 普及组] 龙虎斗题解

题目

链接

https://www.luogu.com.cn/problem/P5016

字面描述

题目描述

轩轩和凯凯正在玩一款叫《龙虎斗》的游戏,游戏的棋盘是一条线段,线段上有 n n n 个兵营(自左至右编号 1 ∼ n 1 \sim n 1n),相邻编号的兵营之间相隔 1 1 1 厘米,即棋盘为长度为 n − 1 n-1 n1 厘米的线段。 i i i 号兵营里有 c i c_i ci位工兵。 下面图 1 为 n = 6 n=6 n=6 的示例:

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

一个兵营的气势为:该兵营中的工兵数$ \times $ 该兵营到 m m m 号兵营的距离;参与游戏 一方的势力定义为:属于这一方所有兵营的气势之和。
下面图 2 为 n = 6 , m = 4 n = 6,m = 4 n=6,m=4 的示例,其中红色为龙方,黄色为虎方:

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

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

输入格式

输入文件的第一行包含一个正整数 n n n,代表兵营的数量。

接下来的一行包含 n n n 个正整数,相邻两数之间以一个空格分隔,第 i i i 个正整数代 表编号为 i i i 的兵营中起始时的工兵数量 c i c_i ci

接下来的一行包含四个正整数,相邻两数间以一个空格分隔,分别代表 m , p 1 , s 1 , s 2 m,p_1,s_1,s_2 m,p1,s1,s2

输出格式

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

样例 #1

样例输入 #1
6 
2 3 2 3 2 3 
4 6 5 2
样例输出 #1
2

样例 #2

样例输入 #2
6 
1 1 1 1 1 16 
5 4 1 1

样例输出 #2

1

提示

【输入输出样例 1 说明】

见问题描述中的图 2。
双方以 m = 4 m=4 m=4 号兵营分界,有 s 1 = 5 s_1=5 s1=5 位工兵突然出现在 p 1 = 6 p_1=6 p1=6 号兵营。
龙方的气势为:
2 × ( 4 − 1 ) + 3 × ( 4 − 2 ) + 2 × ( 4 − 3 ) = 14 2 \times (4-1)+3 \times (4-2)+2 \times (4-3) = 14 2×(41)+3×(42)+2×(43)=14
虎方的气势为:
2 × ( 5 − 4 ) + ( 3 + 5 ) × ( 6 − 4 ) = 18 2 \times (5 - 4) + (3 + 5) \times (6 - 4) = 18 2×(54)+(3+5)×(64)=18
当你将手中的 s 2 = 2 s_2 = 2 s2=2 位工兵派往 p 2 = 2 p_2 = 2 p2=2 号兵营时,龙方的气势变为:
14 + 2 × ( 4 − 2 ) = 18 14 + 2 \times (4 - 2) = 18 14+2×(42)=18
此时双方气势相等。

【输入输出样例 2 说明】

双方以 m = 5 m = 5 m=5 号兵营分界,有 s 1 = 1 s_1 = 1 s1=1 位工兵突然出现在 p 1 = 4 p_1 = 4 p1=4 号兵营。
龙方的气势为:
1 × ( 5 − 1 ) + 1 × ( 5 − 2 ) + 1 × ( 5 − 3 ) + ( 1 + 1 ) × ( 5 − 4 ) = 11 1 \times (5 - 1) + 1 \times (5 - 2) + 1 \times (5 - 3) + (1 + 1) \times (5 - 4) = 11 1×(51)+1×(52)+1×(53)+(1+1)×(54)=11
虎方的气势为:
16 × ( 6 − 5 ) = 16 16 \times (6 - 5) = 16 16×(65)=16
当你将手中的 s 2 = 1 s_2 = 1 s2=1 位工兵派往 p 2 = 1 p_2 = 1 p2=1 号兵营时,龙方的气势变为:
11 + 1 × ( 5 − 1 ) = 15 11 + 1 \times (5 - 1) = 15 11+1×(51)=15
此时可以使双方气势的差距最小。

【数据规模与约定】
1 < m < n , 1 ≤ p 1 ≤ n 1 < m < n,1 ≤ p_1 ≤ n 1<m<n,1p1n
对于 20 % 20\% 20% 的数据, n = 3 , m = 2 , c i = 1 , s 1 , s 2 ≤ 100 n = 3,m = 2, c_i = 1, s_1,s_2 ≤ 100 n=3,m=2,ci=1,s1,s2100
另有 20 % 20\% 20% 的数据, n ≤ 10 , p 1 = m , c i = 1 , s 1 , s 2 ≤ 100 n ≤ 10, p_1 = m, c_i = 1, s_1,s_2 ≤ 100 n10,p1=m,ci=1,s1,s2100
对于 60 % 60\% 60% 的数据, n ≤ 100 , c i = 1 , s 1 , s 2 ≤ 100 n ≤ 100, c_i = 1, s_1,s_2 ≤ 100 n100,ci=1,s1,s2100
对于 80 % 80\% 80% 的数据, n ≤ 100 , c i , s 1 , s 2 ≤ 100 n ≤ 100, c_i,s_1,s_2 ≤ 100 n100,ci,s1,s2100
对于 100 % 100\% 100% 的数据, n ≤ 1 0 5 n≤10^5 n105, c i , s 1 , s 2 ≤ 1 0 9 c_i,s_1,s_2≤10^9 ci,s1,s2109

解法

第一种:超普通模拟

思路

  1. 求出势力值
  2. 模拟每个点得出最小值
  3. 比较最小节点

代码实现

#include<bits/stdc++.h>
using namespace std;
#define ll long long 

const int maxn=1e5+10;
const ll inf=1e18;
int n,m;
ll cnt1,cnt2,mi,p1,s1,s2;
ll a[maxn];
int main(){
//1.
	mi=inf;
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
	scanf("%d%lld%lld%lld",&m,&p1,&s1,&s2);
	for(int i=1;i<m;i++)cnt1=cnt1+(m-i)*a[i];
	for(int i=m+1;i<=n;i++)cnt2=cnt2+(i-m)*a[i];
	if(p1>m)cnt2=cnt2+(p1-m)*s1;
	else if(p1<m)cnt1=cnt1+(m-p1)*s1;
//2.
	for(int i=1;i<=n;i++){
		ll x=cnt1,y=cnt2;
		if(i>m)y=y+(i-m)*s2;
		else if(i<m)x=x+(m-i)*s2;
		if(abs(x-y)<mi)mi=abs(x-y);
	}
//3.
	for(int i=1;i<=n;i++){
		ll x=cnt1,y=cnt2;
		if(i>m)y=y+(i-m)*s2;
		else if(i<m)x=x+(m-i)*s2;
		if(abs(x-y)==mi){
			printf("%d\n",i);
			break;
		}
	}
	return 0;
} 

第二种:超普通模拟+优化

思路

  1. 求出势力值
  2. 反向遍历n个节点情况,记录O(n)

代码实现

#include<bits/stdc++.h>
using namespace std;
#define ll long long 

const int maxn=1e5+10;
int n,m,ans;
ll cnt1,cnt2,mi,p1,s1,s2;
ll a[maxn];
inline void init(){
	scanf("%d",&n);
	for(int i=1;i<=n;i++)scanf("%lld",&a[i]);
	scanf("%d%lld%lld%lld",&m,&p1,&s1,&s2);
}
int main(){
//1.
	init() ;
	a[p1]+=s1;
	for(int i=1;i<m;i++)cnt1=cnt1+(m-i)*a[i];
	for(int i=m+1;i<=n;i++)cnt2=cnt2+(i-m)*a[i];
	mi=abs(cnt1-cnt2);
//2.
	for(int i=n;i>=1;i--){
		ll x=cnt1,y=cnt2;
		if(i>m)y=y+(i-m)*s2;
		else if(i<m)x=x+(m-i)*s2;
		if(abs(x-y)<=mi){
			mi=abs(x-y);
			ans=i;
		}
	}
	printf("%d\n",ans);
	return 0;
} 

第三种:无遍历=数学+物理

思路

杠杆原理:F1L1=F2L2
定性: s 2 放的位置方向相对于 m ,取决与龙和虎的实力大小 {定性:s2放的位置方向相对于m,取决与龙和虎的实力大小} 定性:s2放的位置方向相对于m,取决与龙和虎的实力大小(未来趋势)
定量:龙和虎的实力差距数值 {定量:龙和虎的实力差距数值} 定量:龙和虎的实力差距数值(具体数值)

  1. 求出龙和虎的实力值
  2. 优化:求出龙和虎实力值后,若进行大小比较分三种情况讨论(>=<),代码复杂,可以将m点作为一个基准点,进行处理。
  3. 处理:用cnt2(虎)- cnt1(龙), 若为正数=>cnt2大,放左边,若为负数=>cnt1大,放右边,若为非负非正数,cnt1==cnt2,输出m即可
  4. 根据杠杆原理mi*s2=(cnt2-cnt1)=>mi=(cnt2-cnt1)/s2%= m i 表示,对于相对 m 的上述距离表示 {mi表示,对于相对m的上述距离表示} mi表示,对于相对m的上述距离表示
  5. 若求出最终的点p2,将mi+m即可的出最终结果
  6. 得出p2后又分三种情况:(1)p2<1 已经达到极限,但未实现cnt1-cnt2==0 ,输出1,(2)同理, p2>n,输出n
  7. 否则将p2进行四舍五入输出(可能是小数)

总结

本题 分为2大部分

  1. 求实力(1)
  2. 求p2(2~7)
NOIP 2018 普及初赛第1028题的题解如下: 题目描述: 给定一个正整数N,要求编写一个程序,计算出它的阶乘N!。阶乘N!是所有小于或等于N的正整数的乘积,且0!定义为1。例如:5! = 5 × 4 × 3 × 2 × 1 = 120。 输入描述: 输入仅包含一个正整数N,其范围为1到20。 输出描述: 输出为计算得到的阶乘N!的值。 解题思路: 1. 使用一个数来存储中间计算结果。 2. 从1开始,依次计算到N的所有整数的阶乘。 3. 每计算出一个数的阶乘,就将其乘到数中,更新数的值。 4. 最终数存储的就是N!的结果。 注意点: - 由于N的范围为1到20,而20!的结果是一个非常大的数,普通的数据类型无法存储,因此需要使用数来模拟大数运算。 - 在实现大数乘法时,需要注意进位的问题。 以下是一个简化的伪代码示例: ``` 输入:N 创建一个足够大的数result用于存储结果 result[0] = 1 // 初始化结果为1 对于i从1到N: carry = 0 // 进位初始化为0 对于j从0到result的长度减1: temp = result[j] * i + carry result[j] = temp % 10 // 更新当前位的值 carry = temp / 10 // 计算新的进位 结束循环 如果carry不为0,则继续添加进位 结束循环 输出result数(从后往前输出,以得到正确的顺序) ``` 实际编程时,需要注意数的索引处理和进位处理,以及在输出时避免在前面输出不必要的零。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

materialistOier

我只是一名ssfoier

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值