ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

【NOI2022省选挑战赛 Contest4 B】取石子(博弈论)

2022-03-01 19:33:54  阅读:184  来源: 互联网

标签:先手 省选 ll 然后 int NOI2022 ans Contest4 sum


取石子

题目链接:NOI2022省选挑战赛 Contest4 B

题目大意

给你一个序列,两个人轮流选头部或尾部拿走那个数,然后双方都要使得自己拿到的数的和尽可能大。
然后问你先手能有的最大和。

思路

首先看到第三个部分分,是一个山谷的形式。

那我们肯定是每次都选两半之间最大的,亦或者是说每次选当前剩下中最大的。
那就是排个序,然后奇数位置的给先手,偶数位置的给后手。

然后考虑如果有小山峰(\(x_i\leqslant x_{i+1}\geqslant x_{i+2}\)),那我们考虑会怎样。
如果选到了这个两边,那中间这个 \(x_{i+1}\) 是肯定会选的,而它因为 \(x_{i+2}\) 是小的,所以选中间的那个人一定会把它留给选 \(x_i\) 的(从另外一边也同理)

那其实我们可以把这三个压成一个数(\(x_i+x_{i+2}-x_{i+1}\))。
然后我们就可以每次对山峰这么做一次,做到只剩山谷,然后再搞。

那显然这样我们直接搞先手选的不太方便,考虑先求出先手比后手多的(\(ans\)),然后你设两个的量是 \(x,y\),全部的量是 \(sum\)。
那 \(x+y=sum,x-y=ans\),然后就 \(x=\dfrac{sum+ans}{2}\)。

代码

#include<cstdio>
#include<algorithm>
#define ll long long

using namespace std;

int n, x, m;
ll a[1000001], ans, sum;

bool cmp(ll x, ll y) {
	return x > y;
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", &x); sum += x;
		a[++m] = x;
		while (m > 2 && a[m - 2] <= a[m - 1] && a[m - 1] >= a[m]) {
			a[m - 2] = a[m - 2] + a[m] - a[m - 1];
			m -= 2;
		}
	}
	sort(a + 1, a + m + 1, cmp);
	
	for (int i = 1; i <= m; i++)
		ans += (i & 1) ? a[i] : -a[i];
	printf("%lld", (ans + sum) / 2);
	
	return 0;
}

标签:先手,省选,ll,然后,int,NOI2022,ans,Contest4,sum
来源: https://www.cnblogs.com/Sakura-TJH/p/15952023.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有