栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

『算法研究日志Day260』(作业包) 分治 · 递归 · 动态规划

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

『算法研究日志Day260』(作业包) 分治 · 递归 · 动态规划

苏晓辉の云端作业备份

第十一周 众数问题
#include 
#include 
#include 
#include 
using namespace std;
const int N = 1e5 + 7;
int a[N];
int zs, cs;
// 1 2 2    2 4 5
void range(int *a, int n, int &l, int &r)
{ // L=2  R=5   -=3没毛病(存储位置-1)
    int mid = n / 2;
    for (l = 0; l < n; l++)
        if (a[l] == a[mid])
            break;
    for (r = l + 1; r < n; r++)
        if (a[r] != a[mid])
            break;
}
void asd(int *a, int n)
{
    int l, r, mid = n >> 1;
    range(a, n, l, r);
    int same = r - l;

    if (same > cs)
        cs = same, zs = a[mid];

    if (l > cs)
        asd(a, l);
    if (n - r > cs)
        asd(a + r, n - r);
}

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", &a[i]);
    sort(a, a + n), asd(a, n);
    printf("众数:%d    总个数(重数):%dn", zs, cs);
    return 0;
}


我不知道我对边界的思考是不是对的,我看网络上同学们采用L+1>CS的写法
在我的思考中我认为L是不用+1的,举例子在注释中


    //划分思想:左侧残部(共l个)如果比当前【最大重数】大
    //才有存在更大重数的可能性,才向下递归
    //关于范围 1 2 2      2 4 5 距离
    // L=1 R=4(实际要搜1  45两块)
    // a:最左侧开始          l=1 搜1
    // a+r:  4号位置开始  n-r=2搜45

整数因子分解问题
#include 
#include 
int res = 0, n;
void asd(int n)
{
    if (n == 1)
        res++;
    for (int i = 2; i <= n; i++)
        if (n % i == 0) //可以整除就向下分解
            asd(n / i);
}

int main()
{
    printf("输入正整数:");
    scanf("%d", &n), asd(n);
    printf("分解总数:%dn", res);
    return 0;
}

这个是看能整除就往下走,如果能多给个数组是有能力输出分解方案的
这里是从小到大枚举拆分方案(例如12从 2*2*3  到3*4);同时涉及全排列问题
(在递归过程中自动解决)

第十二周 3-1最长单调递增子序列
#include 
#include 
#include 
using namespace std;
const int N = 1e3 + 7;
int n, f[N], a[N], res;
//此处F含义:以【第i个数】结尾的上升子序列的最长长度
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]), f[i] = 1;
        //长度至少=1,只有一个数字的情况
        //【枚举·上一个数字】
        for (int j = 1; j < i; j++)
            if (a[j] < a[i]) //如果是【上升】
                f[i] = max(f[i], f[j] + 1);
    }
    for (int i = 1; i <= n; i++)
        res = max(res, f[i]);
    printf("%d", res);
    return 0;
}


3-2及其优化
#include 
#include 
#include 
using namespace std;
const int N = 1e5 + 7;
int n, a[N], q[N];

int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
        scanf("%d", &a[i]);
    int len = 0; //当前最长序列长度
    for (int i = 1; i <= n; i++)
    {
        int l = 0, r = len; //在所有可能的前子串长度中进行寻找
        //也可能自己太小了,导致为0也是可能的s
        while (l < r)
        { // r和l是长度的下标
            int mid = l + r + 1 >> 1;
            if (q[mid] < a[i]) //末端更小,可以接入
                l = mid;
            //为什么可以选到mid的最大值?
            //单调性?二分的思想?
            //二分属性会找到第一个符合这个属性的元素位置
            //此时对应的r就是最大的长度
            else
                r = mid - 1;
        } //寻找接入位置
        // printf("%d  %dn", l, r);
        //【bug预检】:修改左侧二分会出bug
        
        len = max(len, r + 1); //是否更新len
        q[r + 1] = a[i];       // r+1位置末端min更新为ai
    }
    printf("%d", len);
    return 0;
}


 // f[x]:以第x个数结尾的最长上升子序列的长度(改进版本或不涉及)
// q[x]:长度为x的子序列的最小结尾数值是谁(具有递增性质)
//搜索过程采用二分
//【接入措施】:希望前面子序列与当前ai的 “距离” 尽可能近
//也就是在前面找到一个 【小于ai】且【数值最大】 的进行接入
//以此保证接入合法,且对于ai的序列最长

3-1独立任务的最优调度问题
//独立任务最优调度问题
#include 
#include 
#include 
#include 
using namespace std;
const int N = 1e4 + 7;
int a[N], b[N], f[N][N];
int time_a, n, min_t = 0x3f3f3f;
int dp()
{
    for (int i = 1; i <= n; i++)          //每个作业
        for (int j = 0; j <= time_a; j++) // A机器所有时间状态
            f[i][j] = j < a[i] ? f[i - 1][j] + b[i] : min(f[i - 1][j - a[i]], f[i - 1][j] + b[i]);
    //状态不合法:由B转移得来,    否则:AB中取得min进行转移
    // f[i-1][j-a[i]]:第I作业给到A处理,B机器总用时不变,延承
    // f[i-1][j]+b[i]:第I作业给到B处理,A机器总用时不变,加上B处理第I作业的情况
    for (int i = 0; i <= time_a; i++) //扫描,处理完成N个作业时候,
    {
        int t = max(f[n][i], i); // A B机器中最大耗时为真实时间
        if (min_t > t)           //取得真实时间的最小值
            min_t = t;
    }
    return min_t;
}
int main()
{
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
        scanf("%d", &a[i]), time_a += a[i];
    for (int i = 1; i <= n; ++i)
        scanf("%d", &b[i]);
    printf("%dn", dp());
    return 0;
}

3-9圈乘运算

补充の例题 分治

T7-棋盘覆盖

//【P20 2.6棋盘覆盖】
#include 
#include 
#include 
using namespace std;
int num = 0, map[500][500];
void cover(int x, int y, int hx, int hy, int size)
{
    if (size == 1) //自棋盘大小=1,没得玩
        return;
    int s = size / 2, cx = s + x, cy = s + y, t = ++num;

    //【黑点坐标·左上块】
    if (hx < cx && hy < cy)
        cover(x, y, hx, hy, s);
    else
    {
        map[cx - 1][cy - 1] = t;
        cover(x, y, cx - 1, cy - 1, s);
    }
    //【黑点坐标·右上块】
    if (hx < cx && hy >= cy)
        cover(x, cy, hx, hy, s);
    else
    {
        map[cx - 1][cy] = t;
        cover(x, cy, cx - 1, cy, s);
    }
    //【黑点坐标·左下块】
    if (hx >= cx && hy < cy)
        cover(cx, y, hx, hy, s);
    else
    {
        map[cx][cy - 1] = t;
        cover(cx, y, cx, cy - 1, s);
    }
    //【黑点坐标·右下块】
    if (hx >= cx && hy >= cy)
        cover(cx, cy, hx, hy, s);
    else
    {
        map[cx][cy] = t;
        cover(cx, cy, cx, cy, s);
    }
}

int main()
{
    printf("棋盘覆盖,输入【棋盘大小】【黑点坐标】:");
    int n, x, y;
    scanf("%d%d%d", &n, &x, &y);
    map[--x][--y] = 0;
    cover(0, 0, x, y, n);
    for (x = 0; x < n; x++)
    {
        for (y = 0; y < n; y++)
            printf("%dt", map[x][y]);
        printf("nnn");
    }
    return 0;
}


    // s:中间位置(左上块の最右下角)(+1后转 右下块の最左上角)
    // x y:当前棋盘最左上角位置,在实际棋盘中的坐标
    // cx cy:右下块,最左上坐标    t:本次放置的三角块の编号
    // x是行(往下走),y是列(往右走)
    //以下,会发生 3个if和 1个else;不存在黑点的,部署三角块
    //存在黑点的,往此方向进行递归

T8-归并排序

//【归并排序】
#include 
#include 
using namespace std;

void asd(int *a, int left, int right, int mid) //两个有序数列,合成一个有序数列
{
    int x, y, z; // right=0         left=5    总长度6
    int b[right - left + 1];
    for (x = left; x <= right; x++) //乱序的,先存下来
        b[x - left] = a[x];
    //【指针部署】
    //【左区间首部x】   【右区间首部y】
    //【左右两区】是有序的,依次写回a
    x = left; // mid在【左半区】の末尾
    y = mid + 1;
    for (z = left; z <= right; z++) //区间写回
    {
        if (x > mid) //【左区间】部署完成,全空
            a[z] = b[y++ - left];
        else if (y > right) //【右区间】部署完成,全空
            a[z] = b[x++ - left];
        else if (b[x - left] > b[y - left]) //【右区间】更小
            a[z] = b[y++ - left];
        else if (b[x - left] < b[y - left])
            a[z] = b[x++ - left];
    }
}
void merge(int *a, int left, int right)
{
    if (left >= right)
        return;
    int mid = (left + right) / 2;
    merge(a, left, mid);
    merge(a, mid + 1, right);
    asd(a, left, right, mid); //合并!
}

int main()
{
    int a[105] = {7, 25, 43, 18, 67, 71, 76, 24, 30, 19, 85, 64, 17, 75, 42, 53, 86, 41}, n = 18, i;
    merge(a, 0, n - 1);
    for (i = 0; i < n; i++)
        printf("%d ", a[i]);
    return 0;
}

T9-快排

#include 
#include 
using namespace std;
#define N 100010
int node[N], n;
void swap(int &s1, int &s2)
{
    int k = s1;
    s1 = s2, s2 = k;
}
void asd(int a[], int l, int r)
{
    if (l >= r)
        return;
    int x = a[l + r >> 1], s1 = l - 1, s2 = r + 1;
    while (s1 < s2)
    {
        do
            s1++;
        while (a[s1] < x); //左侧靠近
        do
            s2--;
        while (a[s2] > x); //右侧靠近
        if (s1 < s2)       //没有溢出
            swap(a[s1], a[s2]);
    }
    asd(a, l, s2), asd(a, s2 + 1, r);
}

int main()
{
    scanf("%d", &n);
    for (int x = 0; x < n; x++)
        scanf("%d", &node[x]);
    asd(node, 0, n - 1);
    for (int x = 0; x < n; x++)
        printf("%d ", node[x]);
    return 0;
}

T10-循环赛程

//【循环赛日程表P35 2.11】
#include 
#include 
using namespace std;
int main()
{
    //循环赛日程表
    //整体是从上往下按照【行の拓展】,每轮行数*2
    int k, n = 1, a[513][513] = {}, m = 1;
    int x, y, i, j, t;
    printf("循环赛日程表,输入选倍数:");
    scanf("%d", &k);
    for (x = 1; x < k; x++)
        n *= 2;
    t = n * 2;
    for (x = 1; x <= n * 2; x++)
        a[1][x] = x;
    for (x = 1; x <= k; x++) // 总轮次
    {
        // x=1  n=4(需要处理的小块个数)  m(工作区中点)
        // y(当前正在处理の小块标号)
        for (y = 1; y <= n; y++) //本次需要处理的方块总数
        {
            for (i = m + 1; i <= 2 * m; i++)
            {
                for (j = m + 1; j <= 2 * m; j++) //上半区域对下半区域开展覆盖
                {
                    // ij下注模拟【小块四分后の右下块】坐标
                    // x=1 y=3(处理第三个方块) n=4  m=1
                    // m恰好是【四分块】小块的长度
                    a[i][j + (y - 1) * m * 2] = a[i - m][j + (y - 1) * m * 2 - m]; //(左上方四分块给到右下角)
                    a[i][j + (y - 1) * m * 2 - m] = a[i - m][j + (y - 1) * m * 2]; //(右上方四分块给到左下角)
                    //左侧是【右下角四分块】
                    //减去一个四分块长度即可
                    //乘以2是因为你要跨越大块,而不是四二分之一块
                }
            }
        }
        n /= 2;
        m *= 2;
    }

    for (x = 1; x <= t; x++)
    {
        for (y = 1; y <= t; y++)
            printf("%dt", a[x][y]);
        printf("nnn");
    }
    return 0;
}

T11-最大子段

//【最大子段问题】·分治算法P58
#include 
#include 
using namespace std;
int a[666], n;

void init()
{
    printf("几个数字:");
    scanf("%d", &n);
    for (int x = 1; x <= n; x++)
        scanf("%d", &a[x]);
}
int max(int x, int y)
{
    int sum = 0, c, left, right;
    if (x == y)
        sum = a[x] > 0 ? a[x] : 0; //不给负数?
    else
    {
        c = (x + y) / 2;
        left = max(x, c);      //【左半区】
        right = max(c + 1, y); //【右半区】
        //【中区】
        int xmax = 0, xsum = 0; //中区左半部分最大值
        for (int i = c; i >= x; i--)
        {
            xsum += a[i];
            if (xmax < xsum)
                xmax = xsum;
        }
        int ymax = 0, ysum = 0; //中区有半部分最大值
        for (int i = c + 1; i <= y; i++)
        {
            ysum += a[i];
            if (ymax < ysum)
                ymax = ysum;
        }
        sum = xmax + ymax;

        if (sum < left)
            sum = left;
        if (sum < right)
            sum = right;
    }
    return sum;
}

int main()
{ //最大子段问题·分治算法 O(nlogn)
    // Day68 P58
    init();
    printf("最大和;%d", max(1, n));
    return 0;
}

T12-根号划分

#include 
#include 
#include 
#include 
using namespace std;
#define s(a, l, r) sort(a + l, a + r + 1);
#define g(a, l, r) sort(a + l, a + r + 1, greater());
const int N = 1e6 + 7;
int a[N], b[N], n;
void show()
{
    for (int x = 1; x <= n; x++)
        printf("%d ", a[x]);
}
void fire()
{
    s(a, 1, n);
    show();
    printf("n");
    g(a, 1, n);
    show();
}
void merge(int *a, int l, int r)
{
    if (l < r)
    {
        int t = (int)sqrt(l - r + 1); //例如 l=1 r=16 出来结果应该是4
        if (t > 1)                    //至少2元素
        {
            //再往下分割成merge
            for (int x = 0; x < t; x++) //这里t是向下取整的,要么=4刚好全跑完
                merge(a, l + x * t, l + (x + 1) * t);
            merge(a, l + t * t, r); //要么就是有剩余一部分尾巴收尾处理
            //注意!末尾特判,因为可能不够凑出整个sqrt
        }
        s(a, l, r); //改成g就是从大到小排序
    }
}
int main()
{
    printf("输入启动:n"), scanf("%d", &n);
    for (int x = 1; x <= n; x++) //从1开始存
        scanf("%d", &a[x]);
    // fire(); // STL排序,复杂度nlogn
    merge(a, 1, n), show();
    return 0;
}

T13-整数因子分解

#include 
#include 
int res = 0, n;
void asd(int n)
{
    if (n == 1)
        res++;
    for (int i = 2; i <= n; i++)
        if (n % i == 0) //可以整除就向下分解
            asd(n / i);
}

int main()
{
    printf("输入正整数:");
    scanf("%d", &n), asd(n);
    printf("分解总数:%dn", res);
    return 0;
}

T13-众数问题

#include 
#include 
#include 
#include 
using namespace std;
const int N = 1e5 + 7;
int a[N];
int zs, cs;
// 1 2 2    2 4 5
void range(int *a, int n, int &l, int &r)
{ // L=2  R=5   -=3没毛病(存储位置-1)
    int mid = n / 2;
    for (l = 0; l < n; l++)
        if (a[l] == a[mid])
            break;
    for (r = l + 1; r < n; r++)
        if (a[r] != a[mid])
            break;
}
void asd(int *a, int n)
{
    int l, r, mid = n >> 1;
    range(a, n, l, r);
    int same = r - l;

    if (same > cs)
        cs = same, zs = a[mid];

    if (l > cs)
        asd(a, l);
    if (n - r > cs)
        asd(a + r, n - r);
    //划分思想:左侧残部(共l个)如果比当前【最大重数】大
    //才有存在更大重数的可能性,才向下递归
    //关于范围 1 2 2      2 4 5 距离
    // L=1 R=4(实际要搜1  45两块)
    // a:最左侧开始          l=1 搜1
    // a+r:  4号位置开始  n-r=2搜45
}

int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 0; i < n; i++)
        scanf("%d", &a[i]);
    sort(a, a + n), asd(a, n);
    printf("众数:%d    总个数(重数):%dn", zs, cs);
    return 0;
}


动态规划

T1-矩阵连乘

//【矩阵连乘】·P46 动态规划 3.1
#include 
#include 
using namespace std;
#define max 0xfffffff
#define N 2745
int n, p[N + 1], dp[N + 1][N + 1], way[N + 1][N + 1];

void init()
{
    printf("输入【矩阵个数】:");
    scanf("%d", &n);
    int x, y;
    for (x = 0; x <= n; x++) //《初始化》
    {
        for (y = 0; y <= n; y++)
        {
            way[x][y] = 0;
            dp[x][y] = max;
        }
    }
    printf("输入【行列情况】:"); //共【n+1】个数字
    for (x = 0; x <= n; x++)
    {
        scanf("%d", &p[x]);
        dp[x][x] = 0; //自己不能乘以自己
    }
}
void test()
{
    int x, y;
    for (x = 1; x <= n; x++)
    {
        for (y = 1; y <= n; y++)
        {
            if (dp[x][y] == max)
                printf("Tt");
            else
                printf("%dt", dp[x][y]);
        }
        printf("nnn");
    }
    printf("nnn");
    for (x = 1; x <= n; x++)
    {
        for (y = 1; y <= n; y++)
        {
            printf("%dt", way[x][y]);
        }
        printf("nnn");
    }
}

void Find_Answer()
{
    int x, y, z, t, min, temp;
    for (x = 2; x <= n; x++) //【分区大小】从2到n
    {
        for (y = 1; y <= n; y++) //【分区块·起始点】··结束点位置或出现溢出情况
        {
            z = x + y - 1; //例如n=5  x=3  y=2 起始点2分3点即234 结束点4=3+2-1
            // z【分区块·末端点 】
            if (z > n) //末端点·溢出
                break;
            for (t = y; t < z; t++) //【寻找·切入位置】【切入起始点后方】y-z之间,寻找t后切入
            {
                min = dp[y][z]; //【前端行】【切入区】【后端列】
                temp = dp[y][t] + dp[t + 1][z] + p[y - 1] * p[z] * p[t];
                if (min > temp)
                {
                    min = dp[y][z] = temp;
                    way[y][z] = t;
                }
            }
        }
    }
    printf("最小乘积次数:%dn", dp[1][n]);
}
void Find_Way(int x, int y)
{
    if (x == y)
        return;
    int k = way[x][y];
    Find_Way(x, k);
    Find_Way(k + 1, y);
    printf("A[%d,%d] * A[%d,%d]n", x, k, k + 1, y);
}

int main()
{
    init();
    Find_Answer();
    test();
    Find_Way(1, n);
    return 0;
}

 最长公共子序列

//【最长公共子序列】P54 3.3
#include 
#include 
#include 
using namespace std;
char a[500], b[500], dp[500][500] = {}, way[500][500];

void work()
{
    printf("启动输入:");
    scanf("%s%s", &a, &b);
    for (int x = 1; x <= strlen(a); x++)
    {
        for (int y = 1; y <= strlen(b); y++)
        {
            if (a[x - 1] == b[y - 1])
            {
                dp[x][y] = dp[x - 1][y - 1] + 1;
                way[x][y] = 1;
            }
            else if (dp[x - 1][y] > dp[x][y - 1])
            {
                dp[x][y] = dp[x - 1][y];
                way[x][y] = 2;
            }
            else
            {
                dp[x][y] = dp[x][y - 1];
                way[x][y] = 3;
            }
        }
    }
}
void Find_road(int x, int y)
{
    if (x == 0 || y == 0)
        return;
    if (way[x][y] == 1)
    {
        Find_road(x - 1, y - 1);
        printf("%c ", a[x - 1]);
    }
    else if (way[x][y] == 2)
        Find_road(x - 1, y);
    else
        Find_road(x, y - 1);
}
void show()
{
    for (int x = 1; x <= strlen(a); x++)
    {
        for (int y = 1; y <= strlen(b); y++)
        {
            printf("%dt", dp[x][y]);
        }
        printf("nnn");
    }

    printf("n");

    for (int x = 1; x <= strlen(a); x++)
    {
        for (int y = 1; y <= strlen(b); y++)
        {
            printf("%dt", way[x][y]);
        }
        printf("nnn");
    }
    printf("子串情况:");
    Find_road(strlen(a), strlen(b));
}

int main()
{
    work();
    show();

    return 0;
}

最大连续子段和 3种版本

//【拓展·最大子矩阵】暴力·制作测试样例使用
#include 
#include 
using namespace std;
int main()
{
    printf("输入启动:");
    int m, n, a[20][20];
    scanf("%d%d", &m, &n);
    for (int x = 1; x <= m; x++)
        for (int y = 1; y <= n; y++)
            scanf("%d", &a[x][y]);

    int max = (1 << 31), sum, t[4];
    for (int x = 1; x <= m; x++)
    {
        for (int y = 1; y <= n; y++) //确定起点
        {
            for (int x1 = x; x1 <= m; x1++)
            {
                for (int y1 = y; y1 <= n; y1++) //确定终点
                {
                    sum = 0;
                    for (int i = x; i <= x1; i++)
                        for (int j = y; j <= y1; j++)
                            sum += a[i][j];
                    if (sum > max)
                    {
                        t[0] = x;
                        t[1] = y;
                        t[2] = x1;
                        t[3] = y1;
                        max = sum;
                    }
                }
            }
        }
    }
    printf("%d <%d %d>--<%d %d>", max, t[0], t[1], t[2], t[3]);
    return 0;
}

//【最大连续字段和P57 3.4】启用dp
#include 
#include 
#include 
#include 
using namespace std;
int main()
{ //最大子段和,动态规划
    int n, a[666];
    printf("输入总数:");
    scanf("%d", &n);
    for (int x = 1; x <= n; x++)
        scanf("%d", &a[x]);
    int b[666] = {}, max = 0, k, q; //【sum】是局部的求和,小于0时候被丢弃
    int dp[666] = {};
    for (int x = 1; x <= n; x++)
    {
        if (dp[x - 1] > 0)            //【简化】dp[x-1]+a[x]>a[x]
            dp[x] = dp[x - 1] + a[x]; //【延续·最大序列和】
        else                          //【更迭·更好的启发点】
        {
            dp[x] = a[x];
            q = x; //起发点
        }          //否则,丢弃sum,更新为当前值

        if (dp[x] > max)
        {
            max = dp[x];
            k = x; //用于存放最大值所处位置
        }
    }
    printf("最大值:%d,起发点%d,终点%d", max, q, k);
    return 0;
}

//【最大の连续子段和】·动态规划·不存于dp
#include 
#include 
#include 
#include 
using namespace std;
int main()
{
    int n, a[666];
    printf("输入总数:");
    scanf("%d", &n);
    for (int x = 1; x <= n; x++)
        scanf("%d", &a[x]);
    int b[666] = {}, sum = 0, max = 0, k, q; //【sum】是局部的求和,小于0时候被丢弃
    for (int x = 1; x <= n; x++)
    {
        if (sum > 0)
            sum += a[x]; //如果大于0,加上当前值
        else
        {
            sum = a[x];
            q = x; //起发点
        }          //否则,丢弃sum,更新为当前值

        if (sum > max)
        {
            max = sum;
            k = x; //用于存放最大值所处位置
        }
    }
    printf("最大值:%d,起发点%d,终点%d", max, q, k);
    return 0;
}

最大子矩阵

//【最大子矩阵】动态规划  多维度转一维
#include 
#include 
using namespace std;
int m, n, a[50][50];
int dp1[50][50] = {};
int xy[4] = {}, maxx = (1 << 31); //起始点·终止点坐标;最大值
void test()
{
    printf("nn【DP1·辅助数组情况】n");
    for (int x = 1; x <= m; x++)
    {
        for (int y = 1; y <= n; y++)
            printf("%dt", dp1[x][y]);
        printf("nnn");
    }
}
void init()
{
    printf("输入参数:");
    scanf("%d%df", &m, &n);
    for (int x = 1; x <= m; x++)
        for (int y = 1; y <= n; y++)
            scanf("%d", &a[x][y]);
    //辅助矩阵生成
    for (int x = 1; x <= m; x++)
    {
        for (int y = 1; y <= n; y++)
        {
            dp1[x][y] += dp1[x - 1][y] + a[x][y]; //上一行加来
        }
    }
    // test();
}
void work()
{
    for (int x = 1; x <= m; x++) //【起始行】
    {
        for (int y = x; y <= m; y++) //【终末行】
        {
            int t[n + 1]; //每列情况
            for (int z = 1; z <= n; z++)
                t[z] = dp1[y][z] - dp1[x - 1][z];
            //【局部转化·为一维状态】
            //下面就和一维处理一样了
            int dp[n + 1] = {}, yy;
            for (int z = 1; z <= n; z++)
            {
                if (dp[z - 1] > 0)            //简化判断式P59
                    dp[z] = dp[z - 1] + t[z]; //【延续】
                else
                {
                    yy = z;
                    dp[z] = t[z]; //【更迭·启动点】
                }
                if (dp[z] > maxx)
                {
                    maxx = dp[z];
                    xy[0] = x;  //行·起始
                    xy[1] = yy; //列·起始
                    xy[2] = y;  //行·终末
                    xy[3] = z;  //列·终末
                }
            }
        }
    }
}
void ans()
{
    printf("最大矩阵和:%d  <%d %d>--<%d %d>n", maxx, xy[0], xy[1], xy[2], xy[3]);
}

int main()
{
    init();
    work();
    ans();
    return 0;
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/917337.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号