P5569 / POJ 1738 一个古老的石头游戏An old Stone Game (GarsiaWachs算法)题解
一个古老的石头游戏An old Stone Game
本题为石子合并super加强版(紫题(或者说原题))。
题目背景相信大家都特别熟悉了,这里按下不表。
首先数据范围中N≤50000,从根本上卡死了区间二维DP。
于是这里引用一种专门供石子合并而开发的新算法————。
GarsiaWachs算法
有关证明请移步OI.wiki
这里阐明做法:
1.寻找最小 \(k\),使 \(a_{k-1} \leq a_{k+1}\)。
2.将 \(a_{k-1}\) 与 \(a_k\) 合并,插入到从 \(k\) 往前数第一个比 \(a_k + a_{k-1}\) 大的数的右侧。
3.将数列中 \(a_{k-1}\) 与 \(a_k\) 删除。
可以这样理解:
假设有三堆石子 \(a,b,c\) 。
case1:先合并 \(a,b\),则总得分为 \(2a+2b+c\)。
case2:先合并 \(b,c\),则总得分为 \(a+2b+2c\)。
显然当 \(a \leq c\) 时,先合并 \(a,b\) 更优。
代码实现
考虑做法中有数据的插入与删除,以及那可怕的数据范围,需要一种插入,删除,查询都很优秀的数据结构。
这里选用 vector 或链表。
将 \(O(n^2)\) 的代码放下:
#include<bits/stdc++.h>
#define Blue_Archive return 0
#define int long long
using namespace std;
const int N=5e4;
const int INF = 0x7f7f7f7f7f7f7f7f;
int n;
int num[N];
int ans;
vector<int> op;
inline int Shiroko()
{
int k = op.size() - 1 - 1;
for(int i = 0;i < k;i ++)
{
if(op[i] <= op[i + 2])
{
k = i;
break;
}
}
int tmp = op[k] + op[k + 1];
op.erase(op.begin() + k);
op.erase(op.begin() + k);
int in = -1;
for(int i = k - 1;i >= 0;i --)
{
if(op[i] > tmp)
{
in = i;
break;
}
}
op.insert(op.begin() + in + 1,tmp);
return tmp;
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
while(cin >> n)
{
if(n == 0) Blue_Archive;
for(int i = 1,x;i <= n;i ++)
{
cin >> x;
op.push_back(x);
}
for(int i = 1;i < n;i ++)
{
ans += Shiroko();
}
cout << ans << "\n";
}
Blue_Archive;
}
然鹅
显然这份 \(n^2\) 代码没有达到原算法 \(O(n \log n)\) 的优秀时间复杂度。
于是我们考虑优化。
参考题解:巨犇
运用dfs进行优化至 \(O(n \log n)\)
下面附上 \(O(n \log n)\) 代码
// 一个古老的石头游戏 An old Stone Game
#include<bits/stdc++.h>
#define Blue_Archive return 0
#define int long long
using namespace std;
const int N=5e4;
const int INF = 0x7f7f7f7f7f7f7f7f;
int n;
int t;
int ans;
int st[N];
inline void Shiroko(int k)
{
int tmp = st[k] + st[k - 1];
ans += tmp;
for(int i = k;i < t - 1;i ++) st[i] = st[i + 1];
t --;
int j = 0;
for(j = k - 1;j > 0 && st[j - 1] < tmp;j --) st[j] = st[j - 1];
st[j] = tmp;
while(j >= 2 && st[j] >= st[j - 2])
{
int d = t - j;
Shiroko(j - 1);
j = t - d;
}
}
signed main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
while(cin >> n)
{
if(n == 0) Blue_Archive;
for(int i = 0;i < n;i ++)
{
cin >> st[i];
}
t = 1;
ans = 0;
for(int i = 1; i < n;i ++)
{
st[t ++] = st[i];
while(t >= 3 && st[t - 3] <= st[t - 1]) Shiroko(t - 2);
}
while(t > 1) Shiroko(t - 1);
cout << ans << "\n";
}
Blue_Archive;
}
与你的日常,便是奇迹

浙公网安备 33010602011771号