SPOJ MINSUN Largest Submatrix [二分+单调栈]

本文针对SPOJ上的MINSUB问题,详细介绍了如何利用单调栈优化算法在O(nm)的时间复杂度内找到面积大于K的子矩阵中的最小值,并确保该最小值为最大值的同时计算面积的最大值。

题目链接:http://www.spoj.com/problems/MINSUB/
—————————————————————————————.
MINSUB - Largest Submatrix
no tags
You are given an matrix M (consisting of nonnegative integers) and an integer K. For any submatrix of M’ of M define min(M’) to be the minimum value of all the entries of M’. Now your task is simple: find the maximum value of min(M’) where M’ is a submatrix of M of area at least K (where the area of a submatrix is equal to the number of rows times the number of columns it has).

Input

The first line contains a single integer T (T ≤ 10) denoting the number of test cases, T test cases follow. Each test case starts with a line containing three integers, R (R ≤ 1000), C (C ≤ 1000) and K (K ≤ R * C) which represent the number of rows, columns of the matrix and the parameter K. Then follow R lines each containing C nonnegative integers, representing the elements of the matrix M. Each element of M is ≤ 10^9

Output

For each test case output two integers: the maximum value of min(M’), where M’ is a submatrix of M of area at least K, and the maximum area of a submatrix which attains the maximum value of min(M’). Output a single space between the two integers.

Example

Input:
2
2 2 2
1 1
1 1
3 3 2
1 2 3
4 5 6
7 8 9

Output:
1 4
8 2
——————————————————————————————.

题目大意:问你找到一个面积大于K的子矩阵中的最小值,在保证最小值最大的情况下计算面积的最大值

解题思路:
首先最小值最大这种就想到了二分,甚至都想到了每次将 >=mid 的值用一表示,其他用0表示,然后枚举子矩阵的左上角和右下角二维树状数组计算值,这样的话总复杂度就到了 O(n4log32n)
然后就不会了
看了wannaflyunion的题解才知道用一个叫做单调栈的东西能在 O(nm) 的复杂度中解决求子矩阵的最大值
想法也同样是每次将 >=mid 的值用一表示,其他用0表示.增加的是在这个过程中处理了每个点向左的1的个数,然后通过维护每一列的时候向上有几个不小于这个值的向下有几个不小于这个值的. 这个过程通过单调栈能做到 O(n) ,然后判断的是 m 列的 所以总复杂度最后只到了O(nmlog2(1e9+1))

因为二分的不同写法 注意下1e9这个值就好了,,,,

附本题代码
—————————————————–.

再次抄袭wannaflyunion的代码..
#include <bits/stdc++.h>

using namespace std;

#define INF        (~(1<<31))
#define INFLL      (~(1ll<<63))
#define pb         push_back
#define mp         make_pair
#define abs(a)     ((a)>0?(a):-(a))
#define lalal      puts("*******");
#define s1(x)      scanf("%d",&x)
#define Rep(a,b,c) for(int a=(b);a<=(c);a++)
#define Per(a,b,c) for(int a=(b);a>=(c);a--)

typedef long long int LL ;
typedef unsigned long long int uLL ;

const int MOD = 1e9+7;
const int N = 100000+5;
const double eps = 1e-6;
const double PI = acos(-1.0);
void fre()
{
    freopen("in.txt","r",stdin);
    freopen("out.txt","w",stdout);
}
/*************************************************/

int n,m,k;
int a[1005][1005],f[1005][1005],U[1005],D[1005];

int check(int x){
    memset(f,0,sizeof(f));
    memset(U,0,sizeof(U));
    memset(D,0,sizeof(D));

    Rep(i,1,n)Rep(j,1,m)
    if(a[i][j]>=x)f[i][j]=f[i][j-1]+1;
    else f[i][j]=0;

    int now ,area = 0;
    Rep(i,1,m){
        Rep(j,1,n){
            now = j-1;
            while(now&&f[j][i]<=f[now][i]) now = U[now];
            U[j] = now ;
        }
        Per(j,n,1){
            now = j+1;
            while(now!=n+1&&f[j][i]<=f[now][i]) now = D[now];
            D[j] = now;
        }
        Rep(j,1,n) area = max(area,(D[j]-U[j]-1)*f[j][i]);
    }
    return area;
}

int main()
{
    int _;
    while(~s1(_)){
        while(_--){
            s1(n),s1(m),s1(k);
            Rep(i,1,n)Rep(j,1,m) s1(a[i][j]);

            int l,r = -1,mid,ans;
            l = 0,r = 1e9+1,ans =0;
            while(l<=r){
                mid = (l+r)>>1;
                if(check(mid)>=k)    ans = mid, l=mid+1;
                else    r=mid-1;
            }
            printf("%d %d\n",ans,check(ans));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值