uva 10827 Maximum sum on a torus

原题:
A grid that wraps both horizontally and vertically is called a torus. Given a torus where each cell contains an integer, determine the sub-rectangle with the largest sum. The sum of a sub-rectangle is the sum of all the elements in that rectangle. The grid below shows
a torus where the maximum sub-rectangle has been shaded.
这里写图片描述
Input
The first line in the input contains the number of test cases (at most 18). Each case starts with an integer N (1 ≤ N ≤ 75) specifying the size of the torus (always square). Then follows N lines describing the torus, each line containing N integers between -100 and 100, inclusive.
Output
For each test case, output a line containing a single integer: the maximum sum of a sub-rectangle within the torus.
Sample Input
2
5
1 -1 0 0 -4
2 3 -2 -3 2
4 1 -1 5 0
3 -2 1 -3 2
-3 2 4 1 -4
3
1 2 3
4 5 6
7 8 9
Sample Output
15
45

中文:
同样是让你找最大子矩阵和,但是四边可以跨边相邻。
送一组数据
3
4 -3 6
-2 -4 -5
7 0 1

答案是18,也就是四个角

ac代码

#include <bits/stdc++.h>
using namespace std;
int t,n,table[400][400],sum[400];
int solve()
{
    int ans=INT_MIN;
    for(int i=1;i<=n;i++)
    {
        for(int j=1;j<=n;j++)
        {
            memset(sum,0,sizeof(sum));
            for(int k=i+1;k<=n+i;k++)
            {
                int tmp=0;
                for(int s=j+1;s<=n+j;s++)
                {
                    sum[s]+=table[k][s];
                    tmp+=sum[s];
                    ans=max(tmp,ans);
                }
            }
        }
    }
    return ans;
}

int main()
{
    ios::sync_with_stdio(false);
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=n;j++)
            {
                cin>>table[i][j];
                table[i+n][j]=table[i][j+n]=table[i+n][j+n]=table[i][j];
            }
        }
        int ans=solve();
        cout<<ans<<endl;
    }
    return 0;
}

tle代码

#include <bits/stdc++.h>
using namespace std;

int table[202][202];
int sum[202][202];
int tmp[202];
int n;
int SubSeq(int a[])
{
    int ans=INT_MIN,tmp;
    for(int i=1;i<=n;i++)
        a[i+n]=a[i];
    for(int k=1;k<n;k++)
    {
        tmp= a[k];
        int s = a[k];
        for(int i = k+1 ;i <= n+k-1 ;i++)
        {
            if( s > 0)
                s += a[i];
            else
                s = a[i];
            tmp = max(tmp , s);
        }
        ans=max(ans,tmp);
    }
    return ans;
}

int solve()
{
    int max1 = INT_MIN;
    int max2 = INT_MAX;
    for(int s=n*2;s>n;s--)
    {
        memset(sum , 0 ,sizeof(sum));
        for(int i = n ;i >= 1; i--)
        {
            for(int j = n ;j >= 1 ;j--)
                sum[i][j] = sum[i+1][j] + table[s-(n-i)][j];
        }

        for(int i = n ;i >= 1 ;i--)
        {
            for(int j  = n+1 ;j > i ;j--)
            {
                for(int k = 1 ;k <= n ;k++)
                    tmp[k] = sum[i][k] - sum[j][k];
                    ans = max(ans , SubSeq(tmp));
            }
        }
    }
    return ans;
}

int main()
{
    ios::sync_with_stdio(false);
    int t;
    cin>>t;
    while(t--)
    {
        cin>>n;
        for(int i = 1 ;i <= n ;i++)
        {
            for(int j = 1 ;j <=n ;j++)
            {
                cin>>table[i][j];
                table[i+n][j]=table[i][j];
            }
        }
    //    cout<<endl;
        cout<<solve()<<endl;
    }
    return 0;
}

思路:

可以跨边的最大子矩阵和,可以联想到环形的最大子段和。
求解环形的最大子段和的思路有两个
比如要求的数据是a[]={11,-3,7}
那么其中一种方案是把a扩展成两倍长度,变为{11,-3,7,11,-3,7},然后每次枚举长度不超过3的最大子段和,时间复杂度为o(n^2)
另外一种方法是找到a的最小子段和,然后用a的所有和减去最小子段和就是环形的最大子段和了,挺好理解。时间复杂为o(n)

那么对于这道题,是不是可以借鉴一下求解环形最大子段和的o(n)算法的思路呢,比如求跨边最大子矩阵和,可以先求出最小子矩阵和,然后再用矩阵的总和减去?可惜方法是错误的,如下图
蓝色圈是最小子矩阵,红色圈的和是该矩阵最大子矩阵的答案。
这里写图片描述

考虑最大子段和的分治方法,可不可以利用此思想把二维矩阵分割来计算?简单的想了一下,首先需要把这个矩阵扩展出4倍来,表示成对边都相邻的情况。然后限定取和的区域大小不超过n*n来运算,时间复杂度大概是n^3logn。不过实现起来貌似很复杂,而且仅仅是个思路而已。

最后也是最粗暴的方法,直接扩展出四倍来,枚举选择区域大小不超过n*n,然后直接计算和取最大值。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值