取石头游戏 c语言,[HNOI2010]取石头游戏(博弈论+贪心)

题目描述:

有\\(n\\)堆石子,每堆石子的个数为\\(a_i\\),保证存在至少一堆石子个数为\\(0\\)

两个人,每个人每次可以取一堆石子,一堆石子可以被取当且仅当它相邻的石子有至少一堆为\\(0\\),第\\(1\\)堆石子的相邻石子堆只有第\\(2\\)堆,第\\(n\\)堆石子的相邻石子堆只有第\\(n-1\\)堆,第\\(i\\)\\((1 < i < n)\\)堆石子的相邻石子堆为第\\(i-1\\)堆和第\\(i+1\\)堆

这两个人都采取最优策略去使自己尽可能多地获得石子,求最后这两个人获得的石子个数分别是多少

\\(2 \\leq n \\leq 10^6\\),\\(0 \\leq a_i \\leq 10^8\\)

蒟蒻题解:

总的石子个数一定,两人都采取最优策略,对于每时每刻当前的先手来说,就是要使后来的所有操作先手的石子个数减去后手的石子个数尽量大

当存在连续\\(3\\)个非\\(0\\)的数\\(a_{i-1}\\),\\(a_i\\),\\(a_{i+1}\\),满足\\(a_i \\geq a_{i-1}\\)且\\(a_i \\geq a_{i+1}\\),要选\\(a_i\\)的话\\(a_{i-1}\\)和\\(a_{i+1}\\)中至少一个要被选

假设先手\\(A\\)选了\\(a_{i-1}\\),此时先手\\(A\\)的最优策略是选\\(a_{i-1}\\),说明选其他的策略对于先手\\(A\\)来说是更不优的,后手\\(B\\)转换为先手时,他肯定不可能去采取那些更不优的策略,且\\(a_i \\geq a_{i+1}\\),在当前局面(不考虑已经被取了的石子)的情况下,他取\\(a_i\\),把\\(a_{i+1}\\)让给现在的后手\\(A\\)一定是最优的策略

然后后手\\(A\\)再转换为先手时,他会采取他当前的最优策略,由于对他来说,除了\\(a_{i+1}\\)之外,其他所有的策略都是没有之前取\\(a_{i-1}\\)来得优的,他采取其他的策略后,后手\\(B\\)一定会采取与那个策略相对应的策略,能获得比取\\(a_i\\)更好的收益效果,而不会取\\(a_{i+1}\\),所以\\(a_{i+1}\\)最后还是会让先手\\(A\\)来取

换句话说,假设\\(a_{i+1}\\)被后手\\(B\\)取了,说明对于后手\\(B\\)来说,取\\(a_{i+1}\\)是他的最优策略,那么\\(B\\)的上一步,\\(A\\)为先手时,\\(A\\)没有取\\(a_{i+1}\\),说明\\(A\\)有一个比取\\(a_{i+1}\\)更优秀的策略,那\\(A\\)完全可以把这个策略提到取\\(a_{i-1}\\)之前,让\\(B\\)去取\\(a_{i-1}\\),自己再取\\(a_i\\),再让\\(B\\)和现在的局势一样,这样的收益对于先手\\(A\\)来说一定更优

所以当\\(a_i \\geq a_{i-1}\\)且\\(a_i \\geq a_{i+1}\\),且先手取\\(a_{i-1}\\)或\\(a_{i+1}\\)时,先手一定会\\(a_{i-1}\\)和\\(a_{i+1}\\)两个都取,后手一定会取\\(a_i\\),可以把这三个石子堆合并起来,石子最多是相对对方尽量多,所以把这三个石子堆合并成\\(a_{i-1}+a_{i+1}-a_i\\)即可

通过上述的操作,可以把每一段用\\(0\\)隔开的子区间变成单调不降、单调不增和\\(V\\)字型

对于不在两边的区间,即中间的区间,不管是单调不降、单调不增还是\\(V\\)字型,要最优,肯定是取当前最大的,不然把当前最大的让给对方一定更不优,而这三种形状中当前最大的都是可以取得到的

对于在两边的区间,对于左边单调不降的部分和右边单调不增的部分,也都如中间的部分一起,也都是可以取得到最大的

至于左边单调不增的部分和右边单调不减的部分,如果是偶数个的话,假设先手\\(A\\)取了左边区间最靠右的石子堆,这是他当前的最优策略,后手\\(B\\)则可以取左边区间第二靠右的石子堆

如果\\(B\\)不是取左边区间第二靠右的石子堆,而是取别的,这样对\\(B\\)更优的话,那么\\(A\\)在之前操作的时候就可以取那个,让\\(B\\)来取左边区间最靠右的石子堆,这对之前的先手\\(A\\)来说肯定更优,所以\\(B\\)一定会接着取左边区间第二靠右的石子堆

这样就可以提前算出来左边区间单调不增部分的答案

如果左边区间单调不增的长度是奇数,可以把中间的转折点分到单调不减那,使得左边区间单调不增的长度是偶数,这样就可以通过上述的方法计算

右边区间单调不减的部分同左边区间单调不增的部分

由于需要排序,我使用的是快排,时间复杂度为\\(\\mathcal O(n log\\ n)\\),当然,你也可以使用归并排序,这样只需要\\(\\mathcal O(n)\\)便能通过此题

参考程序:

#include

using namespace std;

#define Re register int

typedef long long ll;

const int N = 1000005;

int n, siz, r, t, a[N], d[N];

ll ans1, ans2, q[N], g[N];

inline int read()

{

char c = getchar();

int ans = 0;

while (c < 48 || c > 57) c = getchar();

while (c >= 48 && c <= 57) ans = (ans << 3) + (ans << 1) + (c ^ 48), c = getchar();

return ans;

}

inline void sol_pre(int x)

{

if (!x) return;

q[r = 1] = a[1];

for (Re i = 2; i <= x; ++i)

{

q[++r] = a[i];

while (r > 2 && q[r - 1] >= q[r - 2] && q[r - 1] >= q[r]) ans1 += q[r - 1], ans2 += q[r - 1], q[r - 2] += q[r] - q[r - 1], r -= 2;

}

while (r && q[r] >= q[r - 1]) g[++t] = q[r--];

if (r & 1) g[++t] = q[r--];

for (Re i = 1; i <= r; ++i)

if (i & 1) ans2 += q[i];

else ans1 += q[i];

}

inline void sol_nxt(int x)

{

if (x > n) return;

q[r = 1] = a[n];

for (Re i = n - 1; i >= x; --i)

{

q[++r] = a[i];

while (r > 2 && q[r - 1] >= q[r - 2] && q[r - 1] >= q[r]) ans1 += q[r - 1], ans2 += q[r - 1], q[r - 2] += q[r] - q[r - 1], r -= 2;

}

while (r && q[r] >= q[r - 1]) g[++t] = q[r--];

if (r & 1) g[++t] = q[r--];

for (Re i = 1; i <= r; ++i)

if (i & 1) ans2 += q[i];

else ans1 += q[i];

}

inline void sol(int x, int y)

{

if (x > y) return;

q[r = 1] = a[x];

for (Re i = x + 1; i <= y; ++i)

{

q[++r] = a[i];

while (r > 2 && q[r - 1] >= q[r - 2] && q[r - 1] >= q[r]) ans1 += q[r - 1], ans2 += q[r - 1], q[r - 2] += q[r] - q[r - 1], r -= 2;

}

for (Re i = 1; i <= r; ++i) g[++t] = q[i];

}

int main()

{

n = read();

for (Re i = 1; i <= n; ++i)

{

a[i] = read();

if (!a[i]) d[++siz] = i;

}

sol_pre(d[1] - 1), sol_nxt(d[siz] + 1);

for (Re i = 2; i <= siz; ++i) sol(d[i - 1] + 1, d[i] - 1);

sort(g + 1, g + t + 1);

if (t & 1) swap(ans1, ans2);

for (Re i = t; i; --i)

if ((t - i) & 1) ans2 += g[i];

else ans1 += g[i];

printf("%lld %lld", ans1, ans2);

return 0;

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值