POJ1738 An old Stone Game
题意
Time Limit: 5000MS | Memory Limit: 30000K | |
Total Submissions: 4397 | Accepted: 1278 |
Description
At each step of the game,the player can merge two adjoining piles to a new pile.The score is the number of stones in the new pile.
You are to write a program to determine the minimum of the total score.
Input
The last test case is followed by one zero.
Output
Sample Input
1 100 3 3 4 3 4 1 1 1 1 0
Sample Output
0 17 8
Source
分析
参照fanhqme的题解,此题解毫无用处。
石子合并(每次合并相邻的两堆石子,代价为这两堆石子的重量和,把一排石子合并为一堆,求最小代价)
是一个经典的问题。dp可以做到O(n*n)的时间复杂度,方法是:
设f[i,j]为合并从i到j的石子所用最小代价。
f[i,j]=min(sum(i,j)+f[i,k]+f[k+1,j])对所有i<=k<j,其中sum(i,j)表示从i到j的石子重量之和。
设上式取等时k的值为w[i,j],有神牛证明过:w[i,j]>=w[i,j-1],w[i,j]<=w[i+1,j]
这样,枚举k的时候,就有了一个上下界,从而搞掉了一维。
而GarsiaWachs算法可以把时间复杂度压缩到O(nlogn)。
具体的算法及证明可以参见《The Art of Computer Programming》第3卷6.2.2节Algorithm G和Lemma W,Lemma X,Lemma Y,Lemma Z。
只能说一个概要吧:
设一个序列是A[0..n-1],每次寻找最小的一个满足A[k-1]<=A[k+1]的k,(方便起见设A[-1]和A[n]等于正无穷大)
那么我们就把A[k]与A[k-1]合并,之后找最大的一个满足A[j]>A[k]+A[k-1]的j,把合并后的值A[k]+A[k-1]插入A[j]的后面。
有定理保证,如此操作后问题的答案不会改变。
举个例子:
186 64 35 32 103
因为35<103,所以最小的k是3,我们先把35和32删除,得到他们的和67,并向前寻找一个第一个超过67的数,把67插入到他后面
186 64(k=3,A[3]与A[2]都被删除了) 103
186 67(遇到了从右向左第一个比67大的数,我们把67插入到他后面) 64 103
186 67 64 103 (有定理保证这个序列的答案加上67就等于原序列的答案)
现在由5个数变为4个数了,继续!
186 (k=2,67和64被删除了)103
186 131(就插入在这里) 103
186 131 103
现在k=2(别忘了,设A[-1]和A[n]等于正无穷大)
234 186
420
最后的答案呢?就是各次合并的重量之和呗。420+234+131+67=852,哈哈,算对了。
证明嘛,基本思想是通过树的最优性得到一个节点间深度的约束,之后
证明操作一次之后的解可以和原来的解一一对应,并保证节点移动之后他所在的
深度不会改变。详见TAOCP。
具体实现这个算法需要一点技巧,精髓在于不停快速寻找最小的k,即维护一个“2-递减序列”
朴素的实现的时间复杂度是O(n*n),但可以用一个平衡树来优化(好熟悉的优化方法),使得最终复杂度为O(nlogn)
事情并没有结束。
我在poj1738上看到了一个50000个数的石子合并,很痛苦地想:要写平衡树了:(
但是当我把朴素实现的代码
http://cid-354ed8646264d3c4.office.live.com/self.aspx/.Public/1738.cpp
交上去的时候,发现,AC了?!
为什么?
平方的复杂度,50000的数据......
我着手分析。
首先,每次combine()(见源代码)操作的时候,并不一定都会访问到整个数组。
从随机的角度来讲,新合并出来的石子堆相比那些已经合并许许多多次的石子堆来说,
并不是很“牛”。因为他并不很牛,所以j的值也不比k小得了多少。
并且我们维护的“2-递减”序列本身就有一个很强的序的关系,所以从某种感觉上讲,combine()递归调用的次数很少。
这只是一个感性的想法,实际上,它唯一能够提供给我们的想法是:隐藏在O(n*n)里的常数非常小!
有多小?自己测试一下,(我的笔记本比较慢)大约实际时间=0.00000036*n*n毫秒
但即使这样,按照多组数据的时间换算一下,还是应该超时的呀。
看题目最后一句话:
For each test case output the answer on a single line.You may assume the answer will not exceed 1000000000.
这句话等价于:每个数都不会很大(要合并49999次呢!),继续等价于:有好多数是相同的。
即使这样,又有什么不同呢?
当然了!
我绘制了一幅平均每次k-j的值关于n的图像
其中橘红色的列是我生成的随机的实数作为数据测的结果,而蓝色的是随机生成的[1,1024]间的整数测得的结果。
很明显,小范围的数据大大“加速”了算法,甚至可能引起复杂度上的差异。
就是这样,让本该写一个平衡树的题用数组AC了。
呵呵。
呵呵。
代码
#include<iostream>
#define rg register
#define il inline
#define co const
template<class T>il T read(){
rg T data=0,w=1;rg char ch=getchar();
while(!isdigit(ch)) {if(ch=='-') w=-1;ch=getchar();}
while(isdigit(ch)) data=data*10+ch-'0',ch=getchar();
return data*w;
}
template<class T>il T read(rg T&x) {return x=read<T>();}
typedef long long ll;
co int N=5e4+1;
int n,a[N],t,p,ans;
void work(int x){
int k=a[x]+a[x-1];
ans+=k;
for(int i=x;i<t-1;++i) a[i]=a[i+1];
--t;
for(p=x-1;p&&a[p-1]<k;--p) a[p]=a[p-1];
a[p]=k;
while(p>=2&&a[p]>=a[p-2]){
int d=t-p;
work(p-1);
p=t-d;
}
}
void An_old_Stone_Game(){
for(int i=0;i<n;++i) read(a[i]);
t=1;
ans=0;
for(int i=1;i<n;++i){
a[t++]=a[i];
while(t>=3&&a[t-3]<=a[t-1]) work(t-2);
}
while(t>1) work(t-1);
printf("%d\n",ans);
}
int main(){
while(read(n)) An_old_Stone_Game();
return 0;
}
POJ1738 An old Stone Game的更多相关文章
- 【poj1738】 An old Stone Game
http://poj.org/problem?id=1738 (题目链接) 题意 一排n堆石子,合并两堆石子的代价为两堆石子总数之和.问将所有石子合并为一堆所需要的最小代价. Solution 本来想 ...
- POJ1740A New Stone Game[组合游戏]
A New Stone Game Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 5769 Accepted: 3158 ...
- timus 1180. Stone Game 解题报告
1.题目: 1180. Stone Game Time limit: 1.0 secondMemory limit: 64 MB Two Nikifors play a funny game. The ...
- HDU 4048 Zhuge Liang's Stone Sentinel Maze
Zhuge Liang's Stone Sentinel Maze Time Limit: 10000/4000 MS (Java/Others) Memory Limit: 32768/327 ...
- POJ 1740 A New Stone Game
A New Stone Game Time Limit: 1000MS Memory Limit: 30000K Total Submissions: 5453 Accepted: 2989 ...
- Light OJ 1296 - Again Stone Game (博弈sg函数递推)
F - Again Stone Game Time Limit:2000MS Memory Limit:32768KB 64bit IO Format:%lld & %llu ...
- poj 1115 Lifting the Stone 计算多边形的中心
Lifting the Stone Time Limit:1000MS Memory Limit:32768KB 64bit IO Format:%I64d & %I64u S ...
- 【POJ】A New Stone Game(博弈论)
http://poj.org/problem?id=1740 题目大意就是,对于n堆石子,每堆若干个,两人轮流操作,每次操作分两步,第一步从某堆中去掉至少一个,第二步(可省略)把该堆剩余石子的一部分分 ...
- HDUOJ--------A simple stone game(尼姆博弈扩展)(2008北京现场赛A题)
A simple stone game ...
随机推荐
- Learning-MySQL【6】:视图、触发器、存储过程、函数、流程控制
一.视图 视图就是通过查询得到一张虚拟表,然后保存下来,下次用的直接使用即可.使用视图我们可以把查询过程中的临时表摘出来,用视图去实现,这样以后再想操作该临时表的数据时就无需重写复杂的 SQL 语句了 ...
- 通过python的hashlib模块计算一个文件的MD5值
Python的hashlib提供了很多摘要算法,如MD5,SHA1等常用算法. 什么是摘要算法呢?摘要算法又称哈希算法.散列算法.它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(如MD5值 ...
- 【转】Rancher 2.0 里程碑版本:支持添加自定义节点!
原文链接: http://mp.weixin.qq.com/s?__biz=MzIyMTUwMDMyOQ==&mid=2247487533&idx=1&sn=c70258577 ...
- Asp.net core 学习笔记 2.1 升级到 2.2
首先跟着官网 step by step https://docs.microsoft.com/en-us/aspnet/core/migration/21-to-22?view=aspnetcore- ...
- 第 8 章 容器网络 - 071 - 如何定制 Calico 的 IP 池?
定制IP池 首先定义一个 IP Pool,比如: calicoctl create -f ipPool.yml 用此 IP Pool 创建 calico 网络. docker network crea ...
- 【你不一定知晓的】C#取消异步操作
[你不一定知晓的]C#取消异步操作 在.Net和C#中运行异步代码相当简单,因为我们有时候需要取消正在进行的异步操作,通过本文,可以掌握 通过CancellationToken取消任务(包括non-c ...
- linux存储管理之磁盘阵列
磁盘阵列 RAID ====================================================================================RAID:廉 ...
- dedecmsv5.7 ueditor编辑器上传视频/修改,视频显示空白,解决方案
dedecmsv5.7 ueditor 在上传视频之后,显示空白.其实是有视频的,就是显示空白.找了资料,记录一下. 解决方案: 找到include下面的ueditor下面的ueditor.all.j ...
- mybatis的jdbcType和javaType、oracle,MySQL的对应类型
JdbcType介绍 数据库列字段都是有类型的,不同的数据库有不同的类型.为了表示这些数据类型,Java源码是采用枚举来定义的: public enum JDBCType implements SQL ...
- 十年京东Java程序员的工作总结,写给迷茫中的你!
很多年前,刚刚从大学毕业的时候,很多公司来校招.其中最烂俗的一个面试问题是:“你希望你之后三到五年的发展是什么?”.我当时的标准回答是(原话):“成为在某一方面能够独当一面的技术专家“.后来经历了几家 ...