您的位置:首页 > 理论基础 > 计算机网络

ACM学习历程—广东工业大学2016校赛决赛-网络赛E 积木积水(最值问题 || 动态规划)

2016-04-10 20:14 190 查看
题目链接:http://gdutcode.sinaapp.com/problem.php?cid=1031&pid=4

这个题目自然会考虑到去讨论最长或者最短的板子。

笔上大概模拟一下的话,就会知道,假设最长的板子是r,0和n+1位置上都是高度为0的板子,那么对于[0, r-1]中的最长板子rr,rr到r这一短应该都是被深度为a[rr]的水覆盖。同样的[0, rr-1]中的最长板子rrr,rrr到rr这一段应该是被a[rrr]覆盖,以此类推可以搞定r的前面一段,同理搞定后一段。

关于最值这一块,可以使用RMQ之类的logn维护,总的复杂度是nlogn。但是考虑到区间都是[0, r]和[r, n+1]这种的。所以很容易想到DP,p[0][i]表示从0到i区间内最大值的角标,p[1][i]表示从i到n+1区间内最大值的角标。然后两遍方程转移。总的复杂度是O(n)。

代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <algorithm>
#include <set>
#include <map>
#include <queue>
#include <vector>
#include <string>
#define LL long long

using namespace std;

const int maxN = 1e6+5;
int n, a[maxN];

//RMQ-ST算法
//效率nlogn
//查询区间最值,注意区间[0, n-1]和[1, n]的区别
int ma[maxN][20];

void RMQ()
{
memset(ma, 0, sizeof(ma));
for (int i = 0; i <= n+1; ++i)
ma[i][0] = i;
for (int j = 1; (1<<j) <= n+2; ++j)
for (int i = 0; i+(1<<j)-1 <= n+1; ++i)
{
if (a[ma[i][j-1]] > a[ma[i+(1<<(j-1))][j-1]])
ma[i][j] = ma[i][j-1];
else
ma[i][j] = ma[i+(1<<(j-1))][j-1];
}
}

int query(int lt, int rt)
{
int k = 0;
while ((1<<(k+1)) <= rt-lt+1)
k++;
if (a[ma[lt][k]] > a[ma[rt-(1<<k)+1][k]])
return ma[lt][k];
else
return ma[rt-(1<<k)+1][k];
}

void input()
{
scanf("%d", &n);
a[0] = a[n+1] = 0;
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
RMQ();
}

LL cal(int lt, int rt, int h)
{
LL ans = 0;
for (int i = lt; i <= rt; ++i)
ans += max(0, h-a[i]);
return ans;
}

void work()
{
LL ans = 0;
int mid = query(0, n+1), k, now;
now = mid;
while (now > 0)
{
k = query(0, now-1);
ans += cal(k, now-1, a[k]);
now = k;
}
now = mid;
while (now < n+1)
{
k = query(now+1, n+1);
ans += cal(now+1, k, a[k]);
now = k;
}
cout << ans << endl;
}

int main()
{
//freopen("test.in", "r", stdin);
int T;
scanf("%d", &T);
for (int times = 1; times <= T; ++times)
{
input();
work();
}
return 0;
}


View Code
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: