zoukankan      html  css  js  c++  java
  • Atcoder Grand Contest 048 (AGC048) D

    原文链接www.cnblogs.com/zhouzhendong/AGC048D.html

    前言

    我居然更博了???

    题解

    结论1

    对于任何一方来说,他当前要取的那堆的石子个数多多益善。

    证明1

    假设多了 (k) 个石子,那么对于在原先的石子堆中取石子的任意方案,只需要在第一步多取 (k),就对应到新石子堆的一个方案。由此可见新石子堆的决策集合包含了旧石子堆,所以必然不劣。

    结论2

    取石子时,只可能取走一个 或者 取走一堆。

    证明2

    假设没取完一堆,但是取走了大于一个,那不如取一个,因为取一个剩下的石子数较多,肯定不劣。


    如果以事件"某一次某一方取走一堆"为双方最优决策序列的分隔点,那么我们会发现双方一直都在轮流取一个石子。

    这启发我们使用动态规划解决这个问题。

    (vl(L,R)) 表示保留 ([L,R]) 范围的石子,如果左方先手,且在第 (L) 堆石子之前还有一堆大小为 (x(x > 0)) 的石子,那么 (x) 至少为多少才能保证先手必胜。

    类似地,我们定义 (vr(L,R)),表示右方先手,在右侧至少有一堆多大的石子才能保证必胜。

    两部分做法一样,所以这里我们不妨只考虑左方。

    左方第一步有两种决策:拿光一堆、拿一个。

    (vr(L,R-1) > a[R]) 时,显然直接拿光就赢了,所以此时 (lv(L,R) = a[R]+1)

    否则肯定是双方轮流拿一个,直到某一方发现这一次如果拿光的话对方就输了。设左侧石子堆大小为 (x),则左方在拿走 (x - vr(L,R-1) + 1) 个石子后,右方拿一堆,右方获胜;或者右方在拿走 (a[R] - vl(L,R-1) + 1) 个石子后,左方拿一堆,左方获胜。所以左方获胜的条件时 (x - vr(L,R-1) + 1 > a[R] - vl(L,R-1) + 1),即 (vr(L,R) = min(x) = a[R] - vl(L,R-1) + vr(L,R-1) + 1)
    r
    直接 DP 或者 记忆化 dfs 均可。

    于是我们得到了一个单组数据 (O(n^2)) 的算法,解决了此题。

    代码

    #include <bits/stdc++.h>
    #define clr(x) memset(x,0,sizeof(x))
    #define For(i,a,b) for (int i=(a);i<=(b);i++)
    using namespace std;
    typedef long long LL;
    LL read(){
    	LL x=0,f=0;
    	char ch=getchar();
    	while (!isdigit(ch))
    		f=ch=='-',ch=getchar();
    	while (isdigit(ch))
    		x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    	return f?-x:x;
    }
    const int N=105;
    int n;
    int a[N];
    LL l[N][N],r[N][N];
    LL vr(int L,int R);
    LL vl(int L,int R){
    	if (L>R)
    		return 1;
    	LL &res=l[L][R];
    	LL v=a[R];
    	if (res)
    		return res;
    	LL b=vr(L,R-1);
    	if (b>v)
    		return res=1;
    	LL a=vl(L,R-1);
    	// x - a + 1 > v - b + 1
    	return res=max(1LL,v-b+1+a-1+1);
    }
    LL vr(int L,int R){
    	if (L>R)
    		return 1;
    	LL &res=r[L][R];
    	LL v=a[L];
    	if (res)
    		return res;
    	LL b=vl(L+1,R);
    	if (b>v)
    		return res=1;
    	LL a=vr(L+1,R);
    	// x - a + 1 > v - b + 1
    	return res=max(1LL,v-b+1+a-1+1);
    }
    void solve(){
    	n=read();
    	For(i,1,n)
    		a[i]=read();
    	clr(l),clr(r);
    	puts(vl(2,n)<=a[1]?"First":"Second");
    }
    int main(){
    	int T=read();
    	while (T--)
    		solve();
    	return 0;
    }
    
  • 相关阅读:
    .Net几大优点
    小谈.NET下的缓存。
    ASP.NET 防盗链的实现[HttpHandler]
    给网页加个个性的图标
    四个字节整型转换为IP格式
    在 ASP.NET MVC 中使用带后缀的 URL
    Visual Studio 2008 安装 SP1 后智能提示变英语的修补程序 (KB957507)
    Visual Studio 2008 SP1 安装失败的解决方法
    关于0x0d和0x0a回车换行\r和\n
    图像处理工程师的要求
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/AGC048D.html
Copyright © 2011-2022 走看看