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;
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值