最大子矩阵的拓展—目标最近子矩阵(利用分治法)

1038: Not To The Max

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. 
---------------------------------------------------------------------------------- */ 
Your task here has something similar with the problem mentioned above, while you are to find out a non-empty sub-matrix whose elements sum closest to the given integer T.

Input

The input contains several test cases. 
Each test case begins with three integers n m T, which means there are n rows and m columns in the matrix and our target T as mentioned above. Then n lines follows, each of which consists of m non-negative integers bounded in [0, 10000]. 
You can assume that 0 < n <= 20, 0 < m <= 2000, 0 <= T < 2^15 
You should process all test cases to the end of file.

Output

  For each test case, you are to output a line in the from of "Case #:", # is the test case number indexed from 1, and then another line with the sum of the sub-matrix you found, if there is a tie, you should output the smaller one.

Sample Input

3 3 0
1 1 1
1 1 1
1 1 1

3 3 0
1 1 1
1 1 1
1 1 0

3 3 10
2 2 4
5 2 0
0 0 0

Sample Output

Case 1:
1
Case 2:
0
Case 3:
11

HINT

Source



题意:给定一个矩阵,求一个子矩阵,使其和值为所有子矩阵中里给定值的距离(也就是差值的绝对值)最小。
思路:有了xmu1031最大子矩阵的基础,那么我们很容易想到用最大子矩阵的方法来做此题:类似于最大子矩阵的方法,用一个辅助数组把其转化为一维上的问题:求一个数组的一子段和,使其与给定值的距离最小。此题的核心点,也就在此,求最大子段和可以在O(N)时间内解决,但是现在所求的看起来比较复杂,没那么简单了!但是,仔细一想,我们会发现,题目给定的值都在非负数范围?这是在告诉我们什么样一个信息呢?答案就是,我们可以二分!可以对这个数组进行分治,然后可以在O(N log N)的时间内求出一维数组的解,那么二维也就解决了。
为什么可以二分呢?假设我们已经求得左右区间里与给定值的最小距离,合并区间后,取合并区间的中点,然后往不断取两边的最小值,并求和(由于每个数非负,和值必然越来越大),那么,最小值取法只有两个(仔细想想)。如果每个数可以为负数的话,显然,二分是行不通的。

代码:
#include<bits/stdc++.h>
#define INF 0x7fffffff

using namespace std;

int A[22][2010],F[22][2010],C[2010],dp[2010];
int N,M,T,kase;

void dfs(int L,int R,int &Min,int &ans){//二分求解
    if(L==R){
        if(abs(C[L]-T)<Min) Min=abs((C[L]-T)),ans=C[L];
        else if(abs(C[L]-T)==Min && C[L]<ans) ans=C[L];
        return;
    }
    dfs(L,(L+R)/2,Min,ans);dfs((L+R)/2+1,R,Min,ans);
    int i=(L+R)/2,j=(L+R)/2+1;
    int sum=C[i]+C[j];
    while(i>=L || j<=R){//类似于归并排序的写法
        if(abs(T-sum)<Min) Min=abs(T-sum),ans=sum;
        else if(abs(T-sum)==Min && sum<ans) ans=sum;
        if(sum>T) break;
        if(j>R ||(i>=L && C[i]<C[j])) sum+=C[--i];
        else sum+=C[++j];
    }
}

void solve(){
    int ans,Min=INF;
    for(int i=1;i<=N;i++) for(int j=i;j<=N;j++){
        for(int k=1;k<=M;k++) C[k]=F[j][k]-F[i-1][k];//转化为一维数组求解
        dfs(1,M,Min,ans);//二分求一维数组的解
    }
    printf("Case %d:\n%d\n",kase,ans);
}


int main(){
    //freopen("in.txt","r",stdin);
    kase=1;
    while(cin>>N>>M>>T){
        for(int i=1;i<=N;i++) for(int j=1;j<=M;j++)
            scanf("%d",A[i]+j);
        for(int j=0;j<=M;j++) F[0][j]=0;//最大子矩阵的辅助数组
        for(int j=1;j<=M;j++) for(int i=1;i<=N;i++)
            F[i][j]=F[i-1][j]+A[i][j];
        solve();kase++;
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值