子矩阵操作以及尺取法

本文探讨了尺取法(双指针法)在二维问题中的应用,如求最大连续子矩阵和和连续子序列和不超过k的最长长度。通过将二维问题转化为一维,利用O(n)时间复杂度解决,展示了两种解题思路:枚举+二分和直接二分+枚举。
摘要由CSDN通过智能技术生成

目录

 

尺取法(双指针法)

二维化一维:

一、

二、

码1:

思路:

码2:

二分+枚举


尺取法(双指针法)

顾名思义,像尺子一样取一段,满足条件就继续扩大区间,不满足就剔除后端直到满足,然后前端继续前行。

二维化一维

例如二维求子矩阵最大和,可以枚举行长度O(n^2),然后对于每一列这段和可以理解为1个元素,所有m列就对应了m个元素,也就是化作了怎么用O(n)的时间求最大连续子序列和。

例如二维求最大子矩阵和不超过零的元素个数,同理可以枚举行长度O(n^2),然后对于每一列这段和可以理解为1个元素,所有m列就对应了m个元素,也就是化作了怎么用O(n)的时间求连续子序列和不超过k的最长长度。

一、

可以枚举行长度O(n^2),然后对于每一列这段和可以理解为1个元素,所有m列就对应了m个元素,也就是化作了怎么用O(n)的时间求最大连续子序列和(尺取法)。

#include<algorithm>

#include<iostream>

#include<cstring>

#include<string>

#include<queue>

#include<map>

#include<cstdio>

#include<cmath>

#include<stdlib.h>



using namespace std;

typedef long long ll;

const int INF = 0x3f3f3f3f;

const int maxn = 1e6+50;

int dp[505][505];//dp[i][j]第j列前i项和

int main(){

         int n,m;

         cin >> n >> m;

         for(int i = 1;i <= n;i++){

                  for(int j = 1;j <= m;j++){

                          int x;

                          cin >> x;

                          dp[i][j] = dp[i-1][j]+x;

                  }

         }

         int maxs = -5001;

         for(int i = 1;i <= n;i++){

                  for(int j = i;j <= n;j++){//双重循环枚举高度及其具体位置

                          int rec = 0;

                          for(int k = 1;k <= m;k++){//遍历列数

                                   int x = dp[j][k]-dp[i-1][k];

                                   rec += x;

                                   maxs = max(maxs,rec);//每一次都要进行判断

                                   if(rec < 0) rec = 0;//如果这个位置<0了,不如置零从头再来(证明这个元素让//rec变小了,肯定不能加进去)

                          }

                  }

         }

         cout << maxs << endl;

         return 0;

}

二、

码1:

思路:

可以枚举行长度O(n^2),然后对于每一列这段和可以理解为1个元素,所有m列就对应了m个元素,也就是化作了怎么用O(n)的时间求连续子序列和不超过k的最长长度(尺取法)。

O(n^3)

#include<iostream>

#include<algorithm>

#include<cstdio>

#include<string>

#include<map>

#include<cstring>

#include<stdlib.h>



using namespace std;

typedef long long ll;

const int maxn = 1e6+50;

const int INF = 0x3f3f3f3f;

ll _map[300][300];

ll dp[300][300];//dp[i][j] j lie qian i xiang he

int main(){

         int n,m,l;

         cin >> n >> m >> l;

         int f = 0;

         for(int i = 1;i <= n;i++){

                  for(int j = 1;j <= m;j++){

                          cin >> _map[i][j];

                          if(_map[i][j] > l) f++;

                          dp[i][j] = dp[i-1][j] + _map[i][j];

                  }

         }

         if(f == n*m){

                  cout << "-1"<<endl;

                  return 0;

         }

         int maxs = 0;

         for(int i = 1;i <= n;i++){

                  for(int j = i;j <= n;j++){

                          ll now = 0;

                          int rec = 1;

                          for(int k = 1;k <= m;k++){

                                   now += (dp[j][k]-dp[i-1][k]);

                                   if(now <= l)

                                            maxs = max(maxs,(k-rec+1)*(j-i+1));//每一次都判断(只要符合条件)

                                   if(now > l){

                                            if(k == m) break;//如果不符合了,到末尾了直接break掉,

//如果不是末位,去掉最前端继续向前(尺取法)!!记得标记末端位置,rec++;

                                                     now-=dp[j][rec]-dp[i-1][rec];

                                                     rec++;

                                            continue;

                                   }                                

                          }

                  }

         }

         cout << maxs << endl;

         return 0;

}

码2:

二分+枚举

思路:先枚举高度,在进行二分宽度,在二分while里面进行枚举左上角坐标O(n^3logn)

#include <iostream>

#include <cmath>

#include <algorithm>

#include <cstring>

using namespace std;

typedef long long ll;

ll a[300][300],dp[300][300];

ll n,m,k;

ll bestv;

void f(ll h){

        ll wl=1,wr=m;

        while(wr>=wl){

            ll w=(wl+wr)>>1;

            if(h*w<=bestv){

                wl=w+1;

                continue;

            }

            bool flag2=false;

            for(ll i=1;i+h-1<=n&&!flag2;i++){

                for(ll j=1;j+w-1<=m;j++){

                    ll x=i+h-1;

                    ll y=j+w-1;

                    if(dp[x][y]-dp[x][j-1]-dp[i-1][y]+dp[i-1][j-1]<=k){

                        flag2=true;

                        break;

                    }

                }

            }

            if(flag2){

                bestv=h*w;

                wl=w+1;

            }

            else wr=w-1;

        }

        return ;

}



int main()

{

    cin>>n>>m>>k;

    for(ll i=1;i<=n;i++){

        for(ll j=1;j<=m;j++){

            cin>>a[i][j];

        }

    }  

    dp[0][0]=0;

    for(ll i=1;i<=n;i++)dp[i][0]=0;

    for(ll j=1;j<=m;j++)dp[0][j]=0;

    //预处理前缀和

    for(ll i=1;i<=n;i++){

        for(ll j=1;j<=m;j++){

            dp[i][j]=dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1]+a[i][j];

        }

    }

    bestv=-1;

    for(ll h=1;h<=n;h++){

        f(h);

    }

    cout<<bestv<<endl;

    return 0;

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值