DP第二题:poj1050------To the Max

To the Max
Time Limit: 1000MS Memory Limit: 10000K
Total Submissions: 52190 Accepted: 27587
Description

Given a two-dimensional array of positive and negative integers, a sub-rectangle is any contiguous sub-array of size 1*1 or greater located within the whole array. The sum of a rectangle is the sum of all the elements in that rectangle. In this problem the sub-rectangle with the largest sum is referred to as the maximal sub-rectangle.
As an example, the maximal sub-rectangle of the array:

0 -2 -7 0
9 2 -6 2
-4 1 -4 1
-1 8 0 -2
is in the lower left corner:

9 2
-4 1
-1 8
and has a sum of 15.
Input

The input consists of an N * N array of integers. The input begins with a single positive integer N on a line by itself, indicating the size of the square two-dimensional array. This is followed by N^2 integers separated by whitespace (spaces and newlines). These are the N^2 integers of the array, presented in row-major order. That is, all numbers in the first row, left to right, then all numbers in the second row, left to right, etc. N may be as large as 100. The numbers in the array will be in the range [-127,127].
Output

Output the sum of the maximal sub-rectangle.
Sample Input

4
0 -2 -7 0 9 2 -6 2
-4 1 -4 1 -1

8 0 -2
Sample Output

15

做法有很多,O(n^4)可以过,直接暴力求解,O(n^3)转化成最大子段和求解

暴力求解代码:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const int N = 105;
int sum[N][N];
int a[N][N];
bool judge(int i,int j)
{
    if(i < 0 || j < 0){
        return false;
    }
    else{
        return true;
    }
}

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        for(int i = 0;i < n;++i)
        {
            for(int j = 0;j < n;++j)
            {
                scanf("%d",&a[i][j]);
            }
        }
        for(int i = 0;i < n;++i)
        {
            for(int j = 0;j < n;++j)
            {
                sum[i][j] = a[i][j];
                if(judge(i - 1,j - 1)){
                    sum[i][j] -= sum[i - 1][j - 1];
                }
                if(judge(i - 1,j)){
                    sum[i][j] += sum[i - 1][j];
                }
                if(judge(i,j - 1)){
                    sum[i][j] += sum[i][j - 1];
                }
            }
        }
        int MAX = -1;
        for(int i = 0;i < n;++i)
        {
            for(int j = 0;j < n;++j)
            {
                for(int x = 0;x <= i;++x)
                {
                    for(int y = 0;y <= j;++y)
                    {
                        int z = sum[i][j];
                        if(judge(x - 1,y - 1)){
                            z += sum[x - 1][y - 1];
                        }
                        if(judge(x - 1,j)){
                            z -= sum[x - 1][j];
                        }
                        if(judge(i,y - 1)){
                            z -= sum[i][y - 1];
                        }
                        if(z > MAX){
                            MAX = z;
                        }
                    }
                }
            }
        }
        printf("%d\n",MAX);
    }
    return 0;
}

首先先了解一下最大子串和

N个整数组成的序列a[1],a[2],a[3],…,a[n],求该序列如a[i]+a[i+1]+…+a[j]的连续子段和的最大值。当所给的整数均为负数时和为0。
例如:-2,11,-4,13,-5,-2,和最大的子段为:11,-4,13。和为20。
Input
第1行:整数序列的长度N(2 <= N <= 50000)
第2 - N + 1行:N个整数(-10^9 <= A[i] <= 10^9)
Output
输出最大子段和。
Input示例
6
-2
11
-4
13
-5
-2
Output示例
20

类似的解法:
令b[j]表示以位置 j 为终点的所有子区间中和最大的一个
子问题:如j为终点的最大子区间包含了位置j-1,则以j-1为终点的最大子区间必然包括在其中
如果b[j-1] >0, 那么显然b[j] = b[j-1] + a[j],用之前最大的一个加上a[j]即可,因为a[j]必须包含
如果b[j-1]<=0,那么b[j] = a[j] ,因为既然最大,前面的负数必然不能使你更大

再看这道题,显然是把最大子段和转成二维形式了,先算出i行到j行对应的列的各自的和放在一个数组中s,在两重循环遍历所有组合情况,然后再用一维求最大子段和。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
typedef long long LL;
const int inf = 0x3f3f3f3f;
const int N = 105;
int dp[N][N];
int s[N][N];

int main()
{
    int n;
    while(~scanf("%d",&n))
    {
        memset(dp,0,sizeof(dp));
        memset(s,0,sizeof(s));
        for(int i = 1;i <= n;++i)
        {
            for(int j = 1;j <= n;++j)
            {
                scanf("%d",&dp[i][j]);
            }
        }
        for(int i = 1;i <= n;++i)
        {
            for(int j = 1;j <= n;++j)
            {
                s[j][i] = s[j - 1][i] + dp[j][i];
            }
        }
        int MAX = -1;
        for(int i = 0;i <= n;++i)
        {
            for(int j = i  + 1;j <= n;++j)
            {
            //遍历所有情况组合
                int b = 0;
                for(int k = 1;k <= n;++k)
                {
                    if(b > 0){
                        b += (s[j][k] - s[i][k]);
                    }
                    else{
                        b = s[j][k] - s[i][k];
                    }
                    if(MAX < b){
                        MAX = b;
                    }
                }
            }
        }
        printf("%d\n",MAX);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_36386435/article/details/81275178
今日推荐