【问题描述】
这是一道魔改的奶牛题,所以被称为牛奶题。
wdz的牛奶厂里面有N桶排成一排的牛奶,每一桶牛奶都有一个品质值。我们为wdz定义如下的一种操作:选择两桶相邻的品质值相同(设为X)的牛奶,通过奥妙重重的步骤,合并为一桶品质值为X+1的牛奶并放在原来的位置(此时原来的两桶牛奶已经被取走)。
wdz想要通过若干次上述操作得到一桶品质值尽可能大的牛奶(即最大化 所有牛奶品质值中的最大值 ),但是wdz日理万机非常繁忙,所以他请你来帮忙求出这个最大值。
【输入格式】
输入的第1行一个非负整数n,意义见题目描述。第2到n+1行共n个正整数,第i个数表示第i桶牛奶的品质值,数组保证最初的品质值均为1到40之间的整数(也就是说在操作之后最大值超过40是被允许的)。
【输出格式】
输出一行一个整数表示答案。
【输入样例】
8
3
2
1
1
1
2
3
4
【输出样例】
5
【数据范围】
对于30%的数据,保证n<=2,000
这是一道魔改的奶牛题,所以被称为牛奶题。
wdz的牛奶厂里面有N桶排成一排的牛奶,每一桶牛奶都有一个品质值。我们为wdz定义如下的一种操作:选择两桶相邻的品质值相同(设为X)的牛奶,通过奥妙重重的步骤,合并为一桶品质值为X+1的牛奶并放在原来的位置(此时原来的两桶牛奶已经被取走)。
wdz想要通过若干次上述操作得到一桶品质值尽可能大的牛奶(即最大化 所有牛奶品质值中的最大值 ),但是wdz日理万机非常繁忙,所以他请你来帮忙求出这个最大值。
【输入格式】
输入的第1行一个非负整数n,意义见题目描述。第2到n+1行共n个正整数,第i个数表示第i桶牛奶的品质值,数组保证最初的品质值均为1到40之间的整数(也就是说在操作之后最大值超过40是被允许的)。
【输出格式】
输出一行一个整数表示答案。
【输入样例】
8
3
2
1
1
1
2
3
4
【输出样例】
5
【数据范围】
对于30%的数据,保证n<=2,000
对于100%的数据,保证n<=300,000,最初给出的品质值均为不超过40的正整数。
由于对品质值小的桶进行合并不会限制对品质值大的桶的合并,可以发现合并操作是无后效性的,自然而然地考虑贪心,由小到大合并,每次合并连续相等的一段桶,按长度为奇数/偶数分类讨论。
设合并前桶的品质值为X:
(1)如果连续项长度为偶数(假设有2*k桶),两两合并成k桶,每桶品质值为X+1即可。(每次合并品质值只+1,还能合并的情况之后再考虑)
(2)如果连续项长度为奇数(假设有2*k+1桶),则无论怎样合并,都会单独剩下一桶无法合并,又由于我们是按桶的品质由小到大合并的,则剩下的这一桶以后也不会再被合并了,所以以它作为分隔,其左右两边的桶被划分成两个互不影响的部分。现在的问题是我们合并出了k桶品质值为X+1的桶,我们应该将它们放在分隔桶的右边还是放在左边?事实证明我们事先是不知道该放在哪边的, 所以我们可以选择一种很巧妙的放置方式------我们在左右两边都各放置k桶品质值为X+1, 虽然实际上这是不可能的,但由于分隔桶的划分,这样做对最后的答案并没有影响。
上述操作可以用链表实现,最后遍历所有的桶找出品质值最大的那一桶即可。
另:牛奶桶可能的极大品质值为40+log2(300000),约为58。
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int maxn=300000+5;
const int inf =1e8;
int N;
struct Chain
{
int from;
int v;
int to;
}Bucket[maxn];
void init()
{
scanf("%d",&N);
for(int i=1;i<=N;i++)
{
scanf("%d",&Bucket[i].v);
Bucket[i].from=i-1;
Bucket[i].to=i+1;
}
Bucket[N+1].from=N; //(1)
return;
}
void solve()
{
int i,k,e,mid,cnt,ans;
for(i=1;i<=57;i++)
{
for(k=1;k<=N;k=Bucket[k].to)
if(Bucket[k].v==i)
{
e=k,cnt=0;
while(e<=N&&Bucket[e].v==i)
{
cnt++;
e=Bucket[e].to;
}
e=Bucket[e].from; //如果不写(1)这一步,(2)这一步后e=0,将进入死循环。
if(cnt%2)
{
mid=k;
for(int j=1;j<=(cnt-1)/2;j++) mid=Bucket[mid].to;
for(int p=k;p!=mid;p=Bucket[p].to) Bucket[p].v++;
for(int p=e;p!=mid;p=Bucket[p].from) Bucket[p].v++;
}
else
{
mid=k;
for(int j=1;j<cnt/2;j++) mid=Bucket[mid].to;
Bucket[mid].to=Bucket[e].to;
Bucket[Bucket[e].to].from=mid;
mid=Bucket[mid].to;
for(int p=k;p!=mid;p=Bucket[p].to) Bucket[p].v++;
}
k=e;
}
}
ans=0;
for(k=1;k<=N;k++)
ans=max(ans,Bucket[k].v);
printf("%d\n",ans);
return;
}
int main()
{
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
init();
solve();
return 0;
}