TheBrickTowerMediumDivOne
作者:徐懿
关键词:数学 贪心
题目简述
给定一个长度为的序列。
下文均用代替。
对于一个到的排列,定义。
你需要球出一个到的排列,在使得最小的情况下,的字典序尽可能小。
中元素与均为属于的整数。
约定及泉岭序列
对于一个元素不相同且均为属于的整数的序列,设中最大数的下标为,的长度为。满足为的重排列,。我们称为泉岭序列。
引理一
对于一个泉岭序列,有或。
若引理一不成立,我们可以将取出并置于或,构成。
这与最小矛盾,故引理一成立。
引理二
对于一个泉岭序列。设删去中代表中最大数的下标后形成,为一个泉岭序列。
由引理一,中最大数下标必定在或,它对的贡献恒为中最大数,与其他数独立,若要满足最小,也要最小化,故为泉岭序列。
定理一
对于一个泉岭序列,,使得且。
当时,结论显然成立。
当时,由引理一,中最大数下标必定在或,这与本定理相符。由引理二,在中去掉这个数后得到泉岭序列,归纳得证。
算法
我们可以由定理一,根据字典序的性质,贪心地构造出。
具体地,我们以编号从小到大的顺序依次尝试,只要满足当前这个数的小于上一个数,就将其。最后将没有选入的数按数值为第一关键字递增,序号为第二关键字递增排序,置入答案排列的末尾。
具体细节参见代码。
#include <bits/stdc++.h>
std::vector<int> h,a;
bool cmp(int i,int j)//以数值为第一关键字递增,序号为第二关键字递增排序
{
if(h[i]!=h[j])return h[i]<h[j];
return i<j;
}
class TheBrickTowerMediumDivOne
{
public:
std::vector<int> find(std::vector<int> height)
{
h=height;
a.resize(h.size());
int j=-1;
for(int i=0;i<h.size();i++)
{
if(!i || h[i]<=h[a[j]])a[++j]=i;//前缀不递增的一段
else a[h.size()+j-i]=i;//后缀不递减的一段(乱序)
}
std::sort(a.begin()+j+1,a.end(),cmp);//将后缀排序
return a;
}
};