- 入门ST表算法
解决问题:RMQ
教程:https://www.cnblogs.com/qq965921539/p/9608980.html
关键理解之处:查询之时的核心代码:
int search(int l,int r)
{
int k=(int)(log((double)(r-l+1))/log(2.0));//比如说log(4)=2,log(5)=2,log(6)=2,log(7)=2,log(8)=3,log(9)=3……. 那么我们要查询x到y的最小值。 设len=y-x+1,t=log(len) 根据上面的定理:2^t>len/2
return min(st[l][k],st[r-(1<<k)+1][k]);//x到y的最小值可以表示为min(从x往后2^t的最小值,从y往前2^t的最小值
}
此处查询举例,如果查找长度为7的区间,则查找区间前4位的子区间和后四位的子区间,两个子区间重叠了一个位,而查找长度位5的区间,也是查找区间前4位的子区间和后四位的子区间,虽然还是重叠了,但是总比漏了好,对结果并不会影响。其原理其实加入是查找一个区间长度为N的区间,其会取长度为2^k的长度,这个长度是刚好比N大一点的2的幂次数的一半,所以一定也比N/2大,故取子区间时子区间只会重叠,而不会漏掉母区间中间的元素。
2.HDU3183
参考blog:
https://blog.csdn.net/bjfu170203101/article/details/87950240
如同以上博客所说,有贪心的思路:
贪心思路代码:
#include<bits/stdc++.h>
#define LL long long
#define ms0(x) memset(x,0,sizeof(x))
#define ms-1(x) memset(x,-1,sizeof(x))
bool vis[1003];
using namespace std;
int main()
{
list<int> li;
string str;
while(cin>>str)
{
li.clear();
int m;
cin>>m;
for(int j=0;j<str.size();j++)
li.push_back(str[j]-'0');
list<int>::iterator it=li.begin();
it++;
for(;m>0&&it!=li.end();it++)
{
if(it==li.begin())
continue;
list<int> ::iterator l_it=--it;
it++;
if((*l_it)<=(*it))
{
continue;
}
else
{
m--;
li.erase(l_it);
if(it!=li.begin());
{
it--;
}
}
}
while(m--)
{
it=li.end();
it--;
li.erase(it);
}
it=li.begin();
while(*it==0&&it!=li.end())
{
li.erase(it);
it=li.begin();
}
it=li.begin();
while(it!=li.end())
{
cout<<*it;
it++;
}
if(li.empty())
cout<<"0"<<endl;
else
cout<<endl;
}
}
贪心构造方法都大同小异,感觉有list真的很麻烦,不如直接用string的迭代器,就是可能会慢一点。
第二种思路:抽屉原理+RMQ
这个思路才是亮点,前者贪心的角度,是假设每次只删除一个数,从左到右扫数列,角度是只从一个数。而第二种思路的角度是从区间上看的,从抽屉原理可知,从N个数删掉M个数,并且留下的数顺序不变,且最大,则留下的数中的第一位一定在前M+1位里面,即第0位到第N-M+1,第二位则在第一位的位置之后到第N-M+2位,以此类推到最后。
两个思路的本质都在于,留下的数中每一位的优先级不同,为使得到的数最小,最前位的优先级最大,尽可能让最前位最小,即在最前位能取的范围中,取最小的那个。
题目坑点:比较下表的函数要写成小于等于,即相同大小元素尽可能取左边的那个,因为相同都最小的话,尽可能都留着,否者会漏掉最前面的较小值,而样例中的第三个数据会错。
代码:
#include<bits/stdc++.h>
#define LL long long
#define ms0(x) memset(x,0,sizeof(x))
#define ms-1(x) memset(x,-1,sizeof(x))
const int maxn = 1e3+3;
using namespace std;
//---------the template of Sparse Table---------//
//st表的主体是一个二维数组st[i][j],表示需要查询的数组的从下标i到下标i+2^j - 1的最值
//此处的min_pos是比较两个下表对应元素的大小,返回的小于等于的那个元素的下标,直接比较最小值的话只需直接用min,不用记录下标。
int a[maxn];//the array where u input your data
int st[maxn][20];//Sparse Table
int min_pos(int tmp1,int tmp2)
{
if(a[tmp1]<=a[tmp2])
{
return tmp1;
}
else
{
return tmp2;
}
}
void init(int n)//initialize the table
{
for(int i=0; i<n; i++)
st[i][0]=i;
for(int j=1; (1<<j)<=n; j++)//j means power
{
for(int i=0; i+(1<<j)-1<n; i++)
st[i][j]=min_pos(st[i][j-1],st[i+(1<<(j-1))][j-1]);//transformation equation
}
}
int search(int l,int r)
{
int k=(int)(log((double)(r-l+1))/log(2.0));//比如说log(4)=2,log(5)=2,log(6)=2,log(7)=2,log(8)=3,log(9)=3……. 那么我们要查询x到y的最小值。 设len=y-x+1,t=log(len) 根据上面的定理:2^t>len/2
return min_pos(st[l][k],st[r-(1<<k)+1][k]);//x到y的最小值可以表示为min(从x往后2^t的最小值,从y往前2^t的最小值
}
//---------the template of Sparse Table---------//
int ans[maxn];
int main()
{
ios::sync_with_stdio(false);
string str;
while(cin>>str)
{
int n=str.size();
for(int j=0;j<str.size();j++)
a[j]=str[j]-'0';
init(n);
int m;int cnt=0;
cin>>m;int pos=0;
for(int j=m;j<n;j++)//鸽巢原理
{
pos=search(pos,j);
ans[cnt++]=a[pos];
pos++;
}
bool flag=0;
for(int j=0;j<cnt;j++)
{
if(ans[j]!=0)
{
flag=1;
}
if(flag==1)
cout<<ans[j];
}
if(flag==0||cnt==0)
cout<<"0";
cout<<endl;
}
return 0;
}