5月23日总结
Posted 偶尔爆零的蒟蒻
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了5月23日总结相关的知识,希望对你有一定的参考价值。
感受
刚开始觉得二分就是试探一个值行不行,然后再小点或者再大点,做到后面感觉,这只是很浅显的理解,剩下的题就不知道怎么二分了。这一块,数学和数据结构遇到的比较多,素数筛,单调队列,单调栈,三角函数…
想把之前的专题再做一做,时间是有,但还有别的事,我还是比较喜欢专注于做一件事,堆在一起就很头疼,心有点静不下来。
感觉做过的题,并没有完全吃透,只是明白懂了理解意思了,还差些时间打磨和沉淀。
眼下又到了人生的十字路口,看不清前方到底通往哪里,我也只能选择一条路走下去,如果不行那就返回,继续找下一条路,希望在遍历完所有路之前,我能找到答案。
总结
二分细节
//实数二分
double l=0,r=x,mid;
while(r-l>eps){
mid=(r+l)/2;
if(judge(mid)){
//1)l=mid 再大点
//2)r=mid
}
else{
//1)r=mid 再小点
//2)l=mid
}
}
cout<<r<<endl;//此时r<l,返回较小的
//整数二分
int l=0,r=x,mid,ans;
while(l<=r){
mid=l+((r-l)>>1);//防止越界
if(judge(mid)){
ans=mid;//记录答案
l=mid+1;
}
else{
r=mid-1;
}
}
cout<<ans<<endl;
//[0,n)
int lowb(int a[],int len,int k){//返回大于等于k的第一个元素位置
int l=0,r=len,mid;
while(l<r){//l==r时,[l,r)中无元素
mid=(r+l)/2;
if(a[mid]>k)r=mid;//r是开区间
else if(a[mid]<k)l=mid+1;
else r=mid;//往左边缩小
}
return l;
}
//[0,n)
int upb(int a[],int len,int k){//返回大于k的第一个元素位置
int l=0,r=len,mid;
while(l<r){
mid=(r+l)/2;
if(a[mid]<k)l=mid+1;
else if(a[mid]>k)r=mid;
else l=mid+1;//往右边扩展
}
return r;
}
分割问题,judge函数一般就是计数,如果分得过多,则每次分得数量再大点,否则再小点。
比如,最大化最小间距
//最小间距x
bool pd(int x){
int cnt=1,temp=a[0];
for(int i=1;i<n;i++){
if(a[i]-temp>=x){//大于等于最小距离,就放牛
temp=a[i];
cnt++;
if(cnt>=m)return true;//放的牛过多
}
}
return false;
}
...
while(l<=r){
mid=l+((r-l)>>1);
if(pd(mid)){
ans=mid;
l=mid+1;//在牛数量满足时,距离还可以尽可能大
}
else{
r=mid-1;
}
}
另一种,最小化最大连续区间和
//最大连续区间和x
bool judge(int x){
ll tmp=0,cnt=1;
for(int i=n;i>=1;i--){
tmp+=a[i];
if(tmp>x){
tmp=a[i];
cnt++;//新的一段
}
}
return cnt<=k;//段数小于
}
...
while(l<=r){
mid=l+((r-l)>>1);
if(judge(mid)){
ans=mid;
r=mid-1;//再小点
}
else{
l=mid+1;
}
}
n * m的01矩阵每一行一次操作可循环移动一格,问最少几次操作,可使某一列全为1 记录每行1的位置,然后对两个相邻1之间的0的位置二分,确定离0最近的1的距离,(注意循环移动的处理),可用求模将直线变成环,如%n,1,2,3,...,n-1,0(n点),1,2,...,n-1,或者长度变成2*n-1,类似环形石子合并,那样处理
int n,m;
cin>>n>>m;
bool pd;
int cnt;
for(int i=1;i<=n;i++){
pd=false;cnt=0;
for(int j=1;j<=m;j++){
cin>>a[i][j];
if(a[i][j]=='1'){
pd=true;
pos[i][++cnt]=j;
}
}
if(!pd){cout<<"-1\\n";return 0;}
pos[i][++cnt]=pos[i][1]+m;//当j右边没有1,循环移动,离j最近的1是m-j+pos[i][1](最左边的1),扩展一个虚的点
for(int j=1;j<cnt;j++){//两个相邻1
int mid=(pos[i][j]+pos[i][j+1])/2;//两个1之间位置都是0
cout<<pos[i][j]<<" "<<pos[i][j+1]<<" mid "<<mid<<endl;
//mid可能会大于等于m
for(int k=pos[i][j];k<=pos[i][j+1];k++){
if(k<=mid){//左侧更近
dis[k%m]+=k-pos[i][j];//k%m,变成环处理
}
else{//右侧更近
dis[k%m]+=pos[i][j+1]-k;
}
}
}
}
int ans=inf;
for(int j=0;j<m;j++)
ans=min(ans,dis[j]);
cout<<ans<<endl;
欧拉筛
int p[MAX+5];
int cnt;
//欧拉筛
//原理:每个合数都存在一个最小质因子
void getPrime(){
for(int i=2;i<=MAX;i++){
if(!p[i])p[++cnt]=i;
// 用之前产生的素数判断,逐渐扩展
// 以最小质因子将合数划分
for(int j=1;j<=cnt&&i*p[j]<=MAX;j++){
p[i*p[j]]=1;
if(i%p[j]==0)break;//最小质因子分解的界限
//如果不中断,则i*p[j+1]被p[j+1]分解
//设i=p[j]*k,表示i被最小质因子p[j]分解
//i*p[j+1]=p[j]*k*p[j+1]
//p[j+1]>p[j]
//即用p[j+1]分解不是最小的质因子
//会造成之后某个数重复判断,产生不必要的运算
}
}
}
快速幂
int power(int a,int b){
int res=1;
a%=mod;
while(b){
if(b&1)res=(res*a)%mod;//变偶次方
b>>=1;//二分
a=(a*a)%mod;//二分
}
return res;
}
查找左右不小于当前元素的边界
利用动态规划和并查集的思想,查找每个位置的左右边界,和之前做过的一个题很像,都是二维的,是在求直方图内最大矩形面积问题上扩展的。
利用单调栈也可以实现,但是跑得要慢
while(cin>>n>>m){
ms(cnt,0);
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
scanf("%d",&a[i][j]);
if(a[i][j])
cnt[i][j]=cnt[i-1][j]+1;//向下的j列的最大高度
}
int ans=0;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++)l[j]=r[j]=j;//初始边界
cnt[i][0]=cnt[i][m+1]=-1;//结束边界
for(int j=1;j<=m;j++){//左到右
while(cnt[i][j]<=cnt[i][l[j]-1]){
l[j]=l[l[j]-1];
}
}
for(int j=m;j>=1;j--){//右到左
while(cnt[i][j]<=cnt[i][r[j]+1]){
r[j]=r[r[j]+1];
}
}
for(int j=1;j<=m;j++)
ans=max(ans,(r[j]-l[j]+1)*cnt[i][j]);
}
cout<<ans<<endl;
}
以上是关于5月23日总结的主要内容,如果未能解决你的问题,请参考以下文章