zoukankan      html  css  js  c++  java
  • 九度OJ 1102:最小面积子矩阵 (DP、缓存、剪枝)

    时间限制:1 秒

    内存限制:32 兆

    特殊判题:

    提交:1666

    解决:504

    题目描述:

    一个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
    来源:
    2010年上海交通大学计算机研究生机试真题

    思路:

    暴力求解必然超时。此题在计算过程中有大量重复计算数据,应该进行缓存,有效利用,我的思路是b[i][j]保存第j列前i行的数之和,计算子矩阵和的时候,调用前一个sum与相应的b中元素计算。另外对于area大于当前搜索到的最小area的情况,不需要再搜索。

    利用这些缓存和剪枝,我的代码运行时间从一开始的超时到最后的30ms。

    运行编号 用户 问题 结果 详情 内存 代码长度 耗时 语言 提交时间
    1368679 liangrx06 1102 Accepted 1028KB 1397B 30MS C++ / 代码 / 编辑 17:16:12
    1360553 liangrx06 1102 Accepted 912KB 1170B 140MS C / 代码 / 编辑 17:16:27
    1360547 liangrx06 1102 Accepted 952KB 1094B 730MS C / 代码 / 编辑 17:02:51
    1360543 liangrx06 1102 Wrong Answer 952KB 1042B 740MS C / 代码 / 编辑 16:59:39
    1360542 liangrx06 1102 Wrong Answer 952KB 1044B 800MS C / 代码 / 编辑 16:58:00
    1360535 liangrx06 1102 Memory Limit Exceed 391428KB 1230B 0MS C / 代码 / 编辑 16:48:23
    1360529 liangrx06 1102 Time Limit Exceed 912KB 1198B >1000MS C / 代码 / 编辑 16:36:53
    其他人最好的运行时间是10ms,我没有再深入研究。我的代码应该仍有一定的优化余地。


    代码:

    #include <stdio.h>
     
    #define N 100
     
    int main(void)
    {
        int n, m, k;
        int a[N][N], b[N][N];
        int i, j, ii, jj;
        int min, sum, area;
     
        while (scanf("%d%d%d", &n, &m, &k) != EOF)
        {
            for(i=0; i<n; i++)
            {
                for (j=0; j<m; j++)
                {
                    scanf("%d", &a[i][j]);
                    if (i == 0)
                        b[i][j] = a[i][j];
                    else
                        b[i][j] = b[i-1][j] + a[i][j];
                }
            }
     
            min = N*N+1;
            for(i=0; i<n; i++)
            {
                for (j=0; j<m; j++)
                {
                    for (ii=i; ii<n; ii++)
                    {
                        sum = 0;
                        for (jj=j; jj<m; jj++)
                        {
                            area = (ii-i+1)*(jj-j+1);
                            if (area >= min)
                                break;
                            sum += b[ii][jj];
                            if (i > 0)
                                sum -= b[i-1][jj];
                            if (sum >= k)
                                min = area;
                        }
                    }
                }
            }
     
            if (min == N*N+1)
                min = -1;
            printf("%d
    ", min);
        }
     
        return 0;
    }
    /**************************************************************
        Problem: 1102
        User: liangrx06
        Language: C
        Result: Accepted
        Time:30 ms
        Memory:916 kb
    ****************************************************************/

    代码2:

    另一个人的10ms代码,我看了一下,跟我的代码总体思路不一样,但是利用的缓存粒度相同,而且他没有对面积剪枝,为什么时间这么短呢?

    #include<stdio.h>
    #include<algorithm>
    #include<string.h>
    #include<stack>
    #include<ctype.h>
    #include<numeric>
    using namespace std;
    stack<int>stk;
    int mat[110][110];
    int h[110];
    int calc(int n,int lim)
    {
        int ret=n+1,tmp;
        int f=0,r=0,sum=0;
        for(r=0;r<n;r++)
        {
            sum+=h[r];
            while(sum>=lim)
            {
                tmp=r-f+1;
                if(tmp<ret)ret=tmp;
                sum-=h[f];
                f++;
            }
        }
        if(ret==n+1)return -1;
        return ret;
    }
    int main()
    {
        int n,m,i,j;
        int k;
        while(scanf("%d%d%d",&n,&m,&k)!=EOF)
        {
            for(i=0;i<n;i++)
            {
                for(j=0;j<m;j++)
                {
                    scanf("%d",&mat[i][j]);
                }
            }
            if(k<=0)
            {
                puts("0");
                continue;
            }
            int ans=-1;
            for(i=0;i<n;i++)
            {
                memset(h,0,sizeof(h));
                for(j=i;j<n;j++)
                {
                    for(int t=0;t<m;t++)
                    {
                        h[t]+=mat[j][t];
                    }
                    int tmp=calc(m,k)*(j-i+1);
                    if(tmp<0)continue;
                    if(ans==-1||tmp<ans)ans=tmp;
                }
            }
            printf("%d
    ",ans);
        }
        return 0;
    }
      
    /**************************************************************
        Problem: 1102
        User: 鬼M
        Language: C++
        Result: Accepted
        Time:10 ms
        Memory:1072 kb
    ****************************************************************/


    编程算法爱好者。
  • 相关阅读:
    AndroidStudio制作个人资料界面模块以及SQLite数据库的使用
    掌握这13个MySQL索引知识点,让你面试通过率翻倍
    获取数据表最后最后访问,修改,更新,扫描时间
    一本彻底搞懂MySQL索引优化EXPLAIN百科全书
    Win10系统下的MySQL5.7.24版本(解压版)详细安装教程
    解决beego在ubuntu下连接mysql与重置mysql密码
    在Windows上安装MySQL
    docker~dockertoolbox的加速器
    Git 安装 on centos7
    centos7.x中安装SQL Server
  • 原文地址:https://www.cnblogs.com/liangrx06/p/5083930.html
Copyright © 2011-2022 走看看