洛谷 3507 [POI2010]GRA-The Minima Game 题解

博客观赏效果更佳

题意简述

给出 n n n个正整数 a 1 , a 2 , . . . a n a_1,a_2,...a_n a1,a2,...an,AB两个人轮流取数,A先取。每次可以取任意多个数,直到N个数都被取走。每次获得的得分为取的数中的最小值,A和B的策略都是尽可能使得自己的得分减去对手的得分更大。在这样的情况下,最终A的得分减去B的得分为多少。(蒯的,洛谷上的)

思路

排序, d p [ i ] = m a x ( d p [ i − 1 ] , a [ i ] − d p [ i − 1 ] ) dp[i]=max(dp[i-1],a[i]-dp[i-1]) dp[i]=max(dp[i1],a[i]dp[i1]),输出 d p [ n ] dp[n] dp[n]

具体思路

无脑把 a a a数组排序,因为显然答案和给定数字的顺序无关。

其次根据这个推性质。(注意此时我们的 a a a已经是从小到大排好序的)。如果我在玩这个的话,那我肯定会取一段连续的出来,否则就会留下一些大的数给对手,无论如何都不划算。连续取的话,则即能保证我的最小值最大,又能保证对面的最大值最小(在取相同数量的情况下)。但是数量多少,就是我们决策的一个关键点。

关于决策:一看就知道是 D P DP DP(因为不会做,又和决策有关,所以考虑瞎jb D P DP DP)。

d p [ i ] dp[i] dp[i]表示我们考虑排好序的 a a a i i i个数的最优答案(就是轮流取数后的最大差值)。
那么首先我们要枚举上一个人走在了那里,设为 j j j。那么由方程: d p [ i ] = m i n { a [ j ] − d p [ j − 1 ] } dp[i]=min\{a[j]-dp[j-1]\} dp[i]=min{a[j]dp[j1]}。注意,这里的先后手不是绝对的,是相对的。也就是时候,我们在考虑 d p [ i ] dp[i] dp[i]的时候, d p [ i ] dp[i] dp[i]是先手-后手的答案,但是 d p [ j − 1 ] dp[j-1] dp[j1]是后手-先手的答案。而我们要求a[j]+先手-后手,所以转移方程式应该是a[j]-dp[j-1],即a[j]-(后手-先手),即a[j]+先手-后手。

然后我们发现这 t m tm tm转移是 O ( n 2 ) O(n^2) O(n2)的。但是 a [ j ] − d p [ j − 1 ] a[j]-dp[j-1] a[j]dp[j1]的值是固定的,所以我们珂以用前缀和优化来优化这个题。

然后我们发现,我们算 d p [ i ] dp[i] dp[i]需要用到 i − 1 i-1 i1个状态,然后 d p [ i − 1 ] dp[i-1] dp[i1]需要用到 i − 1 i-1 i1个状态。其中有 i − 1 i-1 i1个状态是共有的,表达式都一样,而且值也没有改变。所以我们干脆就直接继承 d p [ i − 1 ] dp[i-1] dp[i1]的答案好了,然后多把 j = i j=i j=i的答案 a [ j ] − d p [ j − 1 ] a[j]-dp[j-1] a[j]dp[j1]给算上,求个 m a x max max即珂。

转移方程变为: d p [ i ] = m a x ( d p [ i − 1 ] , a [ i ] − d p [ i − 1 ] ) dp[i]=max(dp[i-1],a[i]-dp[i-1]) dp[i]=max(dp[i1],a[i]dp[i1])

实现注意

需要 l o n g l o n g long long longlong呢!!!

代码:

#include<bits/stdc++.h>
using namespace std;
namespace Flandre_Scarlet
{
	#define N 1666666
	#define int long long 
	#define F(i,l,r) for(int i=l;i<=r;++i)
	#define D(i,r,l) for(int i=r;i>=l;--i)
	#define Fs(i,l,r,c) for(int i=l;i<=r;c)
	#define Ds(i,r,l,c) for(int i=r;i>=l;c)
	#define Tra(i,u) for(int i=G.Start(u),__v=G.To(i);~i;i=G.Next(i),__v=G.To(i))
	#define MEM(x,a) memset(x,a,sizeof(x))
	#define FK(x) MEM(x,0)
	int n,a[N];
	void R1(int &x)
	{
	    x=0;char c=getchar();int f=1;
	    while(c<'0' or c>'9') f=(c=='-')?-1:1,c=getchar();
	    while(c>='0' and c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
	    x=(f==1)?x:-x;
	}
	void Input()
	{
		R1(n);
		F(i,1,n) R1(a[i]);
	}

	int dp[N];
	void Soviet()
	{
		sort(a+1,a+n+1);
		F(i,1,n)
		{
			dp[i]=max(dp[i-1],a[i]-dp[i-1]);
		}
		printf("%lld\n",dp[n]);
	}
	void IsMyWife()
	{
		Input();
		Soviet();
	}
	#undef int //long long 
}
int main()
{
	Flandre_Scarlet::IsMyWife();
	getchar();getchar();
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值