九度oj-1102

题目描述:

一个N*M的矩阵,找出这个矩阵中所有元素的和不小于K的面积最小的子矩阵(矩阵中元素个数为矩阵面积)

输入:

每个案例第一行三个正整数N,M<=100,表示矩阵大小,和一个整数K
接下来N行,每行M个数,表示矩阵每个元素的值

输出:

输出最小面积的值。如果出现任意矩阵的和都小于K,直接输出-1。

样例输入:

4 4 10
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16

样例输出:

1

代码:

本题是一道非常好的题目,在我看来,所有动态规划解得题目都是十分精妙的。

首先,撇开题目不看,来做这样一道题,就是求一维数组里,大于等于给定任意正整数K的最短连续子序列的长度。

也就是说,这个数组里最短的连续多少个数加起来的值会超过K。这个问题,乍一看,会想到一个O(n*2)的解法,两个for循环搞定。

这里贴出一种DP解法,时间复杂度为O(n)

#include <iostream>  
using namespace std;  

int main(){      //找出数组a中大于等于K的最短连续子序列的长度   
    int a[4];  
    for(int i=0;i<4;i++)  
        cin>>a[i];  
    int K;  
    cin>>K;  
    int start = 0,end = 0;  
    int len = sizeof(a)/sizeof(int);  
    int ans = len + 1;  
    int sum = 0;  
    while(end < len){  
        if(sum < K)  
            sum += a[end];  
        while(sum >= K){  
            ans = min(ans,end-start+1);  
            sum -= a[start++];      //当出现和大于K时,start指针后移   
        }  
        end ++;   
    }  
    cout<<ans<<endl;  
    return 0;  
}  

大致思路是,用两个指针初始化指向头部,然后end指针逐渐后移,直到符合条件的子序列出现,记录当前的长度,然后前指针后移,直到sum<K,之后end指针继续后移。一遍下来就可以完成目标。

这里要注意的是,如果for循环里面的sum>=K,一次都没执行,说明所有的数加起来都没K大,特殊情况最好说明下。

好,至此,已经完成了所有的铺垫。

这道比较快捷的方法是,将高维数组降维,也就是说,选出任意两列,将这两列之间每行的元素相加,得到一个一维数组,对这个一维数组运用上面的最短连续子序列和的算法。时间为O(n),选择任意两列的时候,两个for循环,时间为O(n*2)

求出每种情况的子序列的 长度,得到方块数=(j-i+1)*findShorest(), 说明j表示第j列,i表示第i列(j>i)

对于每种情况的方块数,保留一个最小值即可。

#include <iostream>  
#include <cstring>  
using namespace std;  

int matrix[110][110];  
int num[110];  
int N,M;  
int K;  

void merge(int i,int j){  
    for(int p=0;p<N;p++){  
        for(int k=i;k<=j;k++){  
            num[p] += matrix[p][k];  //合并成一维数组   
        }  
    }  
}  

int findShortest(){  
    int start = 0,end = 0;  
    int sum = 0;  
    int len = N;  
    int ans = len + 1;   
    bool flag = false;  
    while(end < len){  
        if(sum < K){  
            sum += num[end];  
        }  
        while(sum >= K){  
            flag = true;  
            ans = min(ans,end-start+1);  
            sum -= num[start++];  
        }  
        end ++;  
    }  
    if(flag)  
        return ans;  
    else  
        return N*M;    //全部加起来值都没超过K   
}  

int main(){  
    while(cin>>N>>M){  
        cin>>K;  
        int sum = 0;  
        for(int i=0;i<N;i++){  
            for(int j=0;j<M;j++){  
                cin>>matrix[i][j];  
                sum += matrix[i][j];  
            }  
        }  
        if(sum < K)  
            cout<<-1<<endl;  
        else{  
            int min_element = N*M;  
            for(int i=0;i<M;i++){  
                for(int j=i;j<M;j++){  
                    memset(num,0,sizeof(num));  
                    merge(i,j);  
                    int temp = findShortest();  
                    temp = (j - i + 1) * temp;  
                    if(temp < min_element)  
                        min_element = temp;  
                }  
            }  
            cout<<min_element<<endl;  
        }     
    }  
    return 0;  
}