UVa 10891(记忆化搜索,递推)Game of Sum
来源:互联网 发布:中控考勤机数据修改 编辑:程序博客网 时间:2024/05/16 23:37
例题28 Sum游戏(Game of Sum, UVa 10891)
有一个长度为n的整数序列, 两个游戏者A和B轮流取数,A先取。 每
次玩家只能从左端或者右端取一个数, 但不能两端都取。 所有数都被取
走后游戏结束, 然后统计每个人取走的所有数之和, 作为各自的得分。
两个人采取的策略都是让自己的得分尽量高, 并且两人都足够聪明, 求A
的得分减去B的得分后的结果。
【输入格式】
输入包含多组数据。 每组数据的第一行为正整数n(1≤n≤100) , 第
二行为给定的整数序列。 输入结束标志为n=0。
【输出格式】
对于每组数据, 输出A和B都采取最优策略的情况下,A的得分减去B
的得分后的结果。
【分析】
整数的总和是一定的, 所以一个人得分越高, 另一个人的得分就越
低。 不管怎么取, 任意时刻游戏的状态都是原始序列的一段连续子序列
(即被两个玩家取剩下的序列) 。 因此, 我们想到用d(i,j) 表示原序
列的第i~j个元素组成的子序列(元素编号为1~n) , 在双方都采取最优
策略的情况下, 先手得分的最大值(只考虑i~j这些元素) 。
状态转移时, 我们需要枚举从左边取还是从右边取以及取多少个。
这等价于枚举给对方剩下怎样的子序列: 是(k,j) (i<k≤j) , 还是
(i, k) (i≤k<j) 。 因此:
其中, sum(i,j) 是元素i到元素j的数之和。 注意, 这里的“0”是“取
完所有数”的决策, 有了它, 方程就不需要显式的边界条件了。
两人得分之和为sum(1,n) , 因此答案是d(1,n) -(sum(1,
n) -d(1,n) ) =2d(1,n) -sum(1,n) 。 注意, sum(i,j) 的计
算不需要循环累加, 可以预处理S[i]为前i个数之和, 则sum(i,j) =S[j]
-S[i-1]。
状态有O(n2) 个, 每个状态有O(n) 个转移, 所以时间复杂度为
O(n3) , 空间复杂度为O(n2) 。 对于本题的规模, 这样的时间复杂度已
经不错了, 但其实还可以进一步改进。 让我们回顾一下状态转移方程:
如果令f(i,j) =min{d(i,j) , d(i+1,j) , …,d(j,
120
j) } , g(i,j) =min{d(i,j) , d(i,j-1) ,…, d(i,i) } , 则
状态转移方程可以写成:
f和g也可以快速递推出来:f(i,j) =min{d(i,j) , f(i+1,
j) } , g(i,j) =min{d(i,j) , g(i,j-1) } , 因此每个f(i,j)
的计算时间都降为了O(1) 。 下面我们用递推(而非记忆化搜索) 的方
法编写。 代码如下。
有一个长度为n的整数序列, 两个游戏者A和B轮流取数,A先取。 每
次玩家只能从左端或者右端取一个数, 但不能两端都取。 所有数都被取
走后游戏结束, 然后统计每个人取走的所有数之和, 作为各自的得分。
两个人采取的策略都是让自己的得分尽量高, 并且两人都足够聪明, 求A
的得分减去B的得分后的结果。
【输入格式】
输入包含多组数据。 每组数据的第一行为正整数n(1≤n≤100) , 第
二行为给定的整数序列。 输入结束标志为n=0。
【输出格式】
对于每组数据, 输出A和B都采取最优策略的情况下,A的得分减去B
的得分后的结果。
【分析】
整数的总和是一定的, 所以一个人得分越高, 另一个人的得分就越
低。 不管怎么取, 任意时刻游戏的状态都是原始序列的一段连续子序列
(即被两个玩家取剩下的序列) 。 因此, 我们想到用d(i,j) 表示原序
列的第i~j个元素组成的子序列(元素编号为1~n) , 在双方都采取最优
策略的情况下, 先手得分的最大值(只考虑i~j这些元素) 。
状态转移时, 我们需要枚举从左边取还是从右边取以及取多少个。
这等价于枚举给对方剩下怎样的子序列: 是(k,j) (i<k≤j) , 还是
(i, k) (i≤k<j) 。 因此:
其中, sum(i,j) 是元素i到元素j的数之和。 注意, 这里的“0”是“取
完所有数”的决策, 有了它, 方程就不需要显式的边界条件了。
两人得分之和为sum(1,n) , 因此答案是d(1,n) -(sum(1,
n) -d(1,n) ) =2d(1,n) -sum(1,n) 。 注意, sum(i,j) 的计
算不需要循环累加, 可以预处理S[i]为前i个数之和, 则sum(i,j) =S[j]
-S[i-1]。
下面是完整代码。 它采用了记忆化搜索的方式, 显得更加自然。
/*状态有O(n)个,每个状态有O(n)个转移,所以时间复杂度为O(n^3),空间复杂度为O(n^2)发现一件事:其实相互博弈就是一个相互递归的过程可以一次取光,不可以不取*/#include<cstdio>#include<iostream>#include<cstring>using namespace std;const int mx=105;int n,a[mx],d[mx][mx],s[mx],vis[mx][mx];int dp(int i,int j){ if(vis[i][j]) return d[i][j]; vis[i][j]=1; int m=0;//是全部取完,此时就不需要显式的边界了 for(int k=i+1; k<=j; k++) m=min(m,dp(k,j)); for(int k=i; k<j; k++) m=min(m,dp(i,k)); return d[i][j]=s[j]-s[i-1]-m;//这个注意是i-1}int main(){ while(scanf("%d",&n)&&n) { for(int i=1; i<=n; i++) { scanf("%d",a+i); s[i]=s[i-1]+a[i]; } memset(vis,0,sizeof(vis)); printf("%d\n",2*dp(1,n)-s[n]);//dp[1,n]-(s[n]-dp[1,n])=2*dp(1,n)-s[n] } return 0;}
状态有O(n2) 个, 每个状态有O(n) 个转移, 所以时间复杂度为
O(n3) , 空间复杂度为O(n2) 。 对于本题的规模, 这样的时间复杂度已
经不错了, 但其实还可以进一步改进。 让我们回顾一下状态转移方程:
如果令f(i,j) =min{d(i,j) , d(i+1,j) , …,d(j,
120
j) } , g(i,j) =min{d(i,j) , d(i,j-1) ,…, d(i,i) } , 则
状态转移方程可以写成:
f和g也可以快速递推出来:f(i,j) =min{d(i,j) , f(i+1,
j) } , g(i,j) =min{d(i,j) , g(i,j-1) } , 因此每个f(i,j)
的计算时间都降为了O(1) 。 下面我们用递推(而非记忆化搜索) 的方
法编写。 代码如下。
//递推解法,时间复杂度O(n^2),神奇的递推啊,推个公式得推多久啊 #include<cstdio>#include<iostream>#include<cstring>using namespace std;const int mx=105;int n,a[mx],d[mx][mx],s[mx],f[mx][mx],g[mx][mx];int main(){while(scanf("%d",&n)&&n){for(int i=1;i<=n;i++){scanf("%d",a+i);s[i]=s[i-1]+a[i];}for(int i=1;i<=n;i++) f[i][i]=g[i][i]=d[i][i]=a[i];//边界for(int L=1;L<n;L++)for(int i=1;i+L<=n;i++){int j=i+L;int m=min(0,min(f[i+1][j],g[i][j-1]));d[i][j]=s[j]-s[i-1]-m;f[i][j]=min(d[i][j],f[i+1][j]);g[i][j]=min(d[i][j],g[i][j-1]); } printf("%d\n",2*d[1][n]-s[n]);}return 0;}
阅读全文
0 0
- UVa 10891(记忆化搜索,递推)Game of Sum
- UVA 10891 Game of Sum dp(记忆化搜索)
- UVA 10891 Game of Sum(记忆化搜索+博弈)
- Game of Sum 记忆化搜索/递推
- Uva 10891 Game of Sum - 区间DP..记忆化搜索
- UVA 10891 Game of Sum 记忆化搜索
- UVa 10891 Game of Sum / 记忆化搜索
- sum游戏 Game of sum uva 10891 动态规划 备忘录(记忆化搜索)
- 区间dp||记忆化搜索 Game of Sum UVA
- 28.uva 10891 Game of Sum 记忆化dp
- UVa-10891 Game of Sum ACM解题报告(递推O(n^2)算法)
- (beginer) UVA 记忆化搜索 UVA 10536 - Game of Euler
- UVA 10891 - Game of Sum(DP)
- UVa 10891 Game of Sum(DP)
- UVA - 10891 - Game of Sum (DP)
- uva 10891 Game of Sum
- UVA 10891 Game of Sum
- UVa 10891 Game of Sum
- hihoCoder1080-更为复杂的买卖房屋姿势
- 异步加载在的js 在开发者工具中加载不出来
- JVM内存区域异常实战
- 智能指针的交叉引用问题及解决方法
- 第九周 leetcode 72. Edit Distance(Hard)
- UVa 10891(记忆化搜索,递推)Game of Sum
- 机器学习中的判别式模型及生成式模型
- hdu-1421 搬寝室
- 把基于web.xml的应用迁移到Spring Boot
- MySql 学习之索引详解
- 最小二乘法小结
- poj3304—Segments
- IO多路转接之epoll
- CSS3变换、过渡、动画效果