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日总结的主要内容,如果未能解决你的问题,请参考以下文章

5月23日总结

5月23日上午学习日志

第七次团队作业:总结博客

3月5日总结

2019年5月23日项目日志

2018年5月6日GDCPC (广东赛区)总结