• homework-02


    GitHub挂了...我会在其恢复的时候上传我的代码的...其实代码就在blog里面...

    已解决...是我自己的问题...之前没有pull一下...结果就上传不上去...

    一. 程序的架构和思路:

    其实吧...这个第二次作业是在第一次作业上的延伸,于是算法应该和第一次的相似,或者说是将二维转化为一维,然后再做...

    但还是从朴素算法开始想吧...

    最最朴素的算法,就是一个六重循环,枚举矩阵的左上和右下两个点的x、y坐标,然后计算这之中的值的和即可。

    #include<iostream>
    #include<cstdio>
    #include<time.h>
    using namespace std;
    int a[1010][1010];
    int main()
    {
        freopen("input.txt","r",stdin);
        clock_t start,finish;
        start = clock();
        double totaltime;
        int m,n;
        int i,j,p,q,k,l,ans,temp = 0;
        scanf("%d,",&m);
        scanf("%d,",&n);
        for (i = 0;i < m;i++)
        for (j = 0;j < n;j++) scanf("%d,",&a[i][j]);
        ans = a[0][0];
        for (i = 0;i < m;i++)
        for (j = 0;j < n;j++)
        for (p = i;p < m;p++)
        for (q = j;q < n;q++)
        {
            temp = 0;
            for (k = i;k <= p;k++)
            for (l = j;l <= q;l++) temp = temp + a[k][l];
            if (temp > ans) ans = temp;
        }
        printf("%d
    ",ans);
        finish = clock();
        totaltime = finish - start;
        printf("%lf
    ",totaltime);
        fclose(stdin);
        return 0;
    }
    View Code

    所以该继续优化了...

    由代码观之,朴素算法时间复杂度为O(n^6)...这个...太大了有点...

    但是,其空间复杂度为1,根据动态规划的基本思想——空间换时间来优化算法。

    先从计算两点间矩阵的和下手。

    我们可以如此优化:记b[i][j]为左上角到(i,j)的矩阵的和。

    因此,(i,j)到(m,n)的矩阵的和就可以表示为:b[m][n]-b[m][j-1]-b[i-1][n]+b[i-1][j-1]。

    #include<iostream>
    #include<cstdio>
    #include<time.h> 
    using namespace std;
    int a[1010][1010];
    int b[1010][1010];
    int main()
    {
        freopen("input.txt","r",stdin);
        clock_t start,finish;
        start = clock();
        double totaltime;
        int m,n;
        int i,j,p,q,ans,temp = 0;
        scanf("%d,",&m);
        scanf("%d,",&n);
        for (i = 1;i <= m;i++)
        for (j = 1;j <= n;j++) scanf("%d,",&a[i][j]);
        for (i = 1;i <= m;i++)
        for (j = 1;j <= n;j++)
        {
            temp = 0;
            for (p = 1;p <= i;p++)
            for (q = 1;q <= j;q++) temp = temp + a[p][q];
            b[i][j] = temp;
        }
        ans = a[1][1];
        for (i = 1;i <= m;i++)
        for (j = 1;j <= n;j++)
        for (p = i;p <= m;p++)
        for (q = j;q <= n;q++)
        {
            temp = b[p][q] - b[p][j - 1] - b[i - 1][q] + b[i - 1][j - 1];
            if (temp > ans) ans = temp;
        }
        printf("%d
    ",ans);
        finish = clock();
        totaltime = finish - start;
        printf("%lf
    ",totaltime);
        fclose(stdin);
        return 0;
    }
    View Code

    优化后,算法时间复杂度为O(n^4),空间复杂度为O(n^2)。

    需要注意的是,这么做读入数据时要从1开始不要从0开始,因为会有i-1的情况,要保证其大于等于0。

    貌似...O(n^4)还是有点大...

    秉承空间换时间的基本方针,可以继续优化。

    这次就需要基于第一次作业了。

    扩大空间,我们用b[i][j][k]表示第k列的第i行到第j行的和,于是我们将其转化为了b[i][j][0],b[i][j][1],...,b[i][j][k]这样一个一维数列

    于是就可以求其最大子序列和了。

    #include<iostream>
    #include<cstdio>
    #include<time.h>
    #include<cstring>
    using namespace std;
    int a[1010][1010];
    int b[101][101][101];
    int max(int x,int y)
    {
        if (x > y) return x;
        else return y;
    }
    int main()
    {
        freopen("input.txt","r",stdin);
        clock_t start,finish;
        start = clock();
        double totaltime;
        int m,n;
        int i,j,k,ans,temp = 0;
        scanf("%d,",&m);
        scanf("%d,",&n);
        for (i = 0;i < m;i++)
        for (j = 0;j < n;j++) scanf("%d,",&a[i][j]);
        ans = a[0][0];
        for (i = 0;i < m;i++)
        for (j = 0;j < n;j++) ans = max(ans,a[i][j]);
        memset(b,0,sizeof(b));
        for (k = 0;k < n;k++)
        {
            for (i = 0;i < m;i++)
            {
                temp = 0;
                for (j = i;j < m;j++)
                {
                    temp = temp + a[j][k];
                    b[i][j][k] = temp;
                }
            }
        }
        for (i = 0;i < m;i++)
        for (j = i;j < m;j++)
        {
            temp = 0;
            for (k = 0;k < n;k++)
            {
                temp = temp + b[i][j][k];
                if (temp > ans) ans = temp;
                else if (temp < 0) temp = 0;
            }
        }
        printf("%d
    ",ans);
        finish = clock();
        totaltime = finish - start;
        printf("%lf
    ",totaltime);
        fclose(stdin);
        return 0;
    }
    View Code

    这样时间复杂度就优化到O(n^3)了,可以解决较大矩阵了,但是,空间复杂度同样到达了O(n^3),可能会爆内存。

    根据第一次优化的方法,我们可以继续优化空间复杂度。

    用b[i][k]-b[j-1][k]来表示b[i][j][k]。

    #include<iostream>
    #include<cstdio>
    #include<time.h>
    #include<cstring>
    using namespace std;
    int a[1010][1010];
    int b[1010][1010];
    int max(int x,int y)
    {
        if (x > y) return x;
        else return y;
    }
    int main()
    {
        freopen("input.txt","r",stdin);
        clock_t start,finish;
        start = clock();
        double totaltime;
        int m,n;
        int i,j,k,ans,temp = 0;
        scanf("%d,",&m);
        scanf("%d,",&n);
        for (i = 1;i <= m;i++)
        for (j = 1;j <= n;j++) scanf("%d,",&a[i][j]);
        ans = a[1][1];
        for (i = 1;i <= m;i++)
        for (j = 1;j <= n;j++) ans = max(ans,a[i][j]);
        memset(b,0,sizeof(b));
        for (j = 1;j <= n;j++)
        {
            temp = 0;
            for (i = 1;i <= m;i++)
            {
                temp = temp + a[i][j];
                b[i][j] = temp;
            }
        }
        for (i = 1;i <= m;i++)
        for (j = i;j <= m;j++)
        {
            temp = 0;
            for (k = 1;k <= n;k++)
            {
                temp = temp + b[j][k] - b[i - 1][k];
                if (temp > ans) ans = temp;
                else if (temp < 0) temp = 0;
            }
        }
        printf("%d
    ",ans);
        finish = clock();
        totaltime = finish - start;
        printf("%lf
    ",totaltime);
        fclose(stdin);
        return 0;
    }
    View Code

    这样,时间复杂度不变,空间复杂度变为了O(n^2),但同第二个算法,读入数据时要从1开始不要从0开始。

    先跳过不规则连通子图...

    对于上下连通的情况,可以在正下方补一个相同的矩阵,这样在两个矩阵中可以实现上下连通的情况。

    需要注意的是这样要求第j行减去第i行要小于等于矩阵的宽。

    #include<iostream>
    #include<cstdio>
    #include<time.h>
    #include<cstring>
    using namespace std;
    int a[1010][1010];
    int b[1010][1010];
    int max(int x,int y)
    {
        if (x > y) return x;
        else return y;
    }
    int main()
    {
        freopen("input.txt","r",stdin);
        clock_t start,finish;
        start = clock();
        double totaltime;
        int m,n;
        int i,j,k,ans,temp = 0;
        scanf("%d,",&m);
        scanf("%d,",&n);
        for (i = 1;i <= m;i++)
        for (j = 1;j <= n;j++)
        {
            scanf("%d,",&a[i][j]);
            a[i + m][j] = a[i][j];
        }
        ans = a[1][1];
        for (i = 1;i <= m;i++)
        for (j = 1;j <= n;j++) ans = max(ans,a[i][j]);
        memset(b,0,sizeof(b));
        for (j = 1;j <= n;j++)
        {
            temp = 0;
            for (i = 1;i <= m * 2;i++)
            {
                temp = temp + a[i][j];
                b[i][j] = temp;
            }
        }
        for (i = 1;i <= m;i++)
        for (j = i;j <= m;j++)
        {
            if ((j - i) <= m)
            {
                temp = 0;
                for (k = 1;k <= n;k++)
                {
                    temp = temp + b[j][k] - b[i - 1][k];
                    if (temp > ans) ans = temp;
                    else if (temp < 0) temp = 0;
                }
            }
        }
        printf("%d
    ",ans);
        finish = clock();
        totaltime = finish - start;
        printf("%lf
    ",totaltime);
        fclose(stdin);
        return 0;
    }
    View Code

    对于左右连通的情况,同样需要向右补一个相同的矩阵,然后并保证所选子矩阵的长不大于原矩阵即可。

    但是,从左向右做记录的时候需要另开一个变量记录长度。

    实在是懒得写了...索性把上下连通的代码的长宽对调,让其从上往下做,完美解决。

    不规则的连通子图,想了想还是没有特别好的办法能够解决32*32的数据。

    最朴素的算法就是枚举每个点的状态,用0和1表示。

    然后dfs判断连通性并找最大子块,这样的时间复杂度是O(2^(m*n)),就是10*10可解,32*32不可解。

    由此想到是否可用状态压缩动态规划做,但是似乎0、1两个状态不够,还需要表示上下连通等其他状态并且似乎转移的时候也有问题。

    于是就要用0,1,...,k来表示状态,稍微算了一下时间复杂度,已经是个天文数字了...

    于是这一问我实在是想不出什么好的算法了...

     二. 程序运行截图:

    自上至下依次为第二至第五个算法的运行结果(第一个O(n^6)的实在是跑不出来了...数据是100*100的):

    由此可见,速度的提升是极其明显的。

    . 回答问题:

    述在这么多相似的需求面前, 你怎么维护你的设计 (父类/子类/基类, UML, 设计模式,  或者其它方法) 让整个程序的架构不至于崩溃的?

    答:我目前是把每个需求分开来写的...如果要合并的话,也许利用case语句能达到要求。

    给出你做单元测试/代码覆盖率的最终覆盖率的报告, 用截屏显示你的代码覆盖率.

    答:我写的程序是面向过程的...貌似没法做单元测试么?

    你在这个作业中学到了什么?  有什么好的设计值得分享?  感想如何 (太容易 / 太难 / 太无趣)?

    答:我尝试去学习状态压缩动态规划,但还是不会做那一问...值得分享的是关于多维问题,可以由简单推复杂,即由一维推多维。

    说实话...那个不规则的最大子图那一问有点难...

     

    Personal Software Process Stages

    时间百分比(%)

    实际花费的时间 (分钟)

    原来估计的时间 (分钟)

    Planning

    计划

         

    ·         Estimate

    ·         估计这个任务需要多少时间,把工作细化并大致排序

     4.8%  30 30

    Development

    开发

         

    ·         Analysis

    ·         需求分析 (包括学习新技术)

     9.6%  60  30

    ·         Design Spec

    ·         生成设计文档

     2.4%  15  10

    ·         Design Review

    ·         设计复审 (和同事审核设计文档)

     0%  0  0

    ·         Coding Standard

    ·         代码规范 (制定合适的规范)

     1.6%  10  10

    ·         Design

    ·         具体设计

     9.6%  60  80

    ·         Coding

    ·         具体编码

     38.4%  240  150

    ·         Code Review

    ·         代码复审

     19.2%  120  50

    ·         Test

    ·         测试(自我测试,修改代码,提交修改)

     6.4%  40  60

    Reporting

    总结报告

         
    • Test Report
    • 测试报告
       3.2% 20  30
    • Size Measurement
    • 计算工作量
    • Postmortem & Improvement Plan
    • 事后总结, 并提出改进
      4.8% 30  30
             
    Total 总计 100%

    总用时

    625

    总估计的用时

    470

  • 相关阅读:
    线程池全面总结
    有状态(Stateful)与无状态(Stateless)
    Callable接口--有返回值的线程
    集合类--最详细的面试宝典--看这篇就够用了(java 1.8)
    [Android App]IFCTT,即:If Copy Then That,一个基于IFTTT的"This"实现
    应朋友死皮白咧地邀请贴一个招聘广告
    [Android]Android焦点流程代码分析
    [Android]Android内存泄漏你所要知道的一切(翻译)
    [Android]Gradle 插件 DiscardFilePlugin(class注入&清空类和方法)
    [Android]使用RecyclerView替代ListView(四:SeizeRecyclerView)
  • 原文地址:https://www.cnblogs.com/yimingzou/p/3348255.html
Copyright © 2020-2023  润新知