hdu 3630 Crystal mine

Crystal mine

Time Limit: 5000/1500 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 528    Accepted Submission(s): 86


Problem Description
Legends always begin with the sentence “Long long ago......”
Long long ago, there is a kingdom. The border of this kingdom is rectangular, and some lakes in it. Old king divides land to N * M areas. So, we can regard this kingdom as a matrix. Every element of matrix is an area. This kingdom is famous for crystal mine, because every land contains an ocean of crystal underground, except lakes.
Now, the old king wants to build a large rectangular mine field in a rectangular range which is decide by him. What’s more, he wants to build a special area in mine field, which can produce crystal manyfold. Notice that mine field can't contain lakes. Unfortunately, the old king is too old to remember something. He always asks his adviser this question with different range.
Adviser is tired of answering those questions day by day. So, you are assigned to solve this problem. You get the kingdom map, and the amount of crystal of every area (as A[][]) has been marked on this map. You should find out the best plan to build this mine field which can produce most crystal. Old king never care the details of plan. The only thing he wants know is the number of crystal the mine field can produce.
 

Input
There are several test cases in the input file.
The first line of input file contains a number T, representing the number of test cases. For each test case:
First line contains four integers separated by a single blank, N (1 <= N <= 150), M (1 <= M <= 150), Q (1<= Q <= 200), S (1 <= S <= 10). The size of matrix is N * M. Q indicates the number of questions of old king. S indicates that, if you set the special area in (i,j), (i,j) will produce S * A[i][j] instead of A[i][j].
The following N lines, each line contains M number, the j-th number of i-th line represent A[i][j] (-1 <= A[i][j] < 100 ). Number -1 indicates that, this area is a lake. Otherwise A[i][j] indicates the amount of crystal in this area.
Following Q line, each line contains four integers p, q, x, y. (1 <= p <= x <= N, 1 <= q <= y <= M) indicating the rectangular range of this question is from (p,q) to (x,y). (p,q) is upper left corner of range, and (x,y) is lower right corner of range.
 

Output
For each test case, first line should contains “Case #: ”, ”#”is case number (starting from 1). Each of the following Q line contains a number indicating the answer of question.
Look at sample output for more details. There is a blank line between two consecutive test cases.
 

Sample Input
  
  
1 4 4 3 2 1 -1 0 3 1 1 2 0 2 1 0 -1 1 4 6 1 1 1 1 1 1 1 3 3 1 3 3 4
 

Sample Output
  
  
Case 1: 2 9 8
题意:给你一个地图 再给你一个矩阵范围 让你在这个矩阵中找到一个不包含-1的子矩阵 且这个子矩阵的和+S*(max-1) 最小 (max 为这个子矩阵的和的最大值)
思路:用最大子矩阵方法枚举出满足条件的矩阵 预处理后能直接对子矩阵求和 然后用二维RMQ求最大值 然后更新ans
感想:其实思路并不难 题目也并不是太难 不过坑了我和我的老大几天额 ╮(╯▽╰)╭ 我们的复杂度降到O(n^3)了 但还是TLE 好蛋疼 感谢网友 ice_crazy 我们是从他的代码中找到了是什么卡了我们的时间 二维RMQ时求log(n)/log(2) 这个打一个表就够了 因为不打表的话就会算O(n^3)次 会很拖时间的  
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;

const int maxn = 155;
int a[maxn][maxn];
int mp[maxn][maxn];
int power[maxn];
int ssum[maxn][maxn];                       // 对二维数组预处理 方便求子矩阵和
int dp[maxn][maxn][9][9];
int h[maxn][maxn],le[maxn][maxn],ri[maxn][maxn];
int n,m,q,ans,sum,s;

int maxx(int xx,int yy)
{
    return xx>yy?xx:yy;
}
void init_rmq()
{
    for(int i=0; (1<<i)<=n; i++)
    {
        for(int j=0; (1<<j)<=m; j++)
        {
            if(i==0 && j==0)
                continue;
            for(int row=1; row+(1<<i)-1<=n; row++)
            {
                for(int col=1; col+(1<<j)-1<=m; col++)
                {
                    if(i==0)
                    {
                        dp[row][col][i][j] =
                            maxx(dp[row][col][i][j-1],dp[row][col+(1<<(j-1))][i][j-1]);
                    }
                    else
                    {
                        dp[row][col][i][j] =
                            maxx(dp[row][col][i-1][j],dp[row+(1<<(i-1))][col][i-1][j]);
                    }
                }
            }
        }
    }
}
int query(int x1,int y1,int x2,int y2)
{
    int kx=power[x2-x1+1];
    int ky=power[y2-y1+1];
    int m1=dp[x1][y1][kx][ky];
    int m2 = dp[x2-(1<<kx)+1][y1][kx][ky];
    int m3 = dp[x1][y2-(1<<ky)+1][kx][ky];
    int m4 = dp[x2-(1<<kx)+1][y2-(1<<ky)+1][kx][ky];
    return maxx( maxx(m1,m2), maxx(m3,m4) );
}
void left(int hang)                   // 向左扩展
{
    int i,j;
    h[hang][0]=-2;
    for(i=1; i<=m; i++)
    {
        if(h[hang][i]>0)
        {
            le[hang][i]=1;
            while(h[hang][i-le[hang][i]]>=h[hang][i])
            {
                le[hang][i]+=le[hang][i-le[hang][i]];
            }
        }
    }
}
void right(int hang)                  // 向右扩展
{
    int i,j;
    h[hang][m+1]=-2;
    for(i=m; i>=1; i--)
    {
        if(h[hang][i]>0)
        {
            ri[hang][i]=1;
            while(h[hang][i+ri[hang][i]]>=h[hang][i])
            {
                ri[hang][i]+=ri[hang][i+ri[hang][i]];
            }
        }
    }
}
void init_maxarrey()                    // 对矩阵做求最大子矩阵的预处理  
{
    int i,j;
    for(i=1; i<=n; i++)
    {
        for(j=1; j<=m; j++)
        {
            if(mp[i][j]>0) h[i][j]=h[i-1][j]+1;
            else h[i][j]=0;
        }
        left(i);
        right(i);
    }
}
void solve(int u1,int v1,int u2,int v2)
{
    int i,j,k,ss;
    int xx1,xx2,yy1,yy2,temps;
    ans=0;
    for(i=u1; i<=u2; i++)
    {
        for(j=v1; j<=v2; j++)
        {
            if(h[i][j]>=0)               // 这里的判断条件不要加错了  我开始加的是 h[i][j]-h[u1][j]>=0 不能这么加 
            {
                xx1=maxx(i-h[i][j]+1,u1);
                yy1=maxx(j-le[i][j]+1,v1);
                xx2=i;
                yy2=j+ri[i][j]-1<v2?j+ri[i][j]-1:v2;
                temps=query(xx1,yy1,xx2,yy2);
                ss=ssum[xx2][yy2]-ssum[xx2][yy1-1]-ssum[xx1-1][yy2]+ssum[xx1-1][yy1-1]+temps*(s-1);
                if(ans<ss) ans=ss;
            }
        }
    }
}
int main()
{
    int i,j,t,t1,k;
    for(i=1;i<=150;i++)                 // 打表很重要
    {
        power[i]=log(double(i))/log(2.0);
    }
    scanf("%d",&t);
    for(k=1; k<=t; k++)
    {
        scanf("%d%d%d%d",&n,&m,&q,&s);
        memset(ssum,0,sizeof(ssum));
        for(i=1; i<=n; i++)
        {
            for(j=1; j<=m; j++)
            {
                scanf("%d",&a[i][j]);
                dp[i][j][0][0]=a[i][j];
                ssum[i][j]=a[i][j]+ssum[i-1][j]+ssum[i][j-1]-ssum[i-1][j-1];
                if(a[i][j]>=0) mp[i][j]=1;
                else mp[i][j]=-1;
            }
        }
        init_rmq();
        init_maxarrey();
        if(k>1) printf("\n");
        printf("Case %d:\n",k);
        while(q--)
        {
            int x1,y1,x2,y2;
            scanf("%d%d%d%d",&x1,&y1,&x2,&y2);
            solve(x1,y1,x2,y2);
            printf("%d\n",ans);
        }
    }
    return 0;
}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值