Hihocoder 1580 Matrix【思维+Dp+RMQ】

351 篇文章 2 订阅

描述

Once upon a time, there was a little dog YK. One day, he went to an antique shop and was impressed by a beautiful picture. YK loved it very much.

However, YK did not have money to buy it. He begged the shopkeeper whether he could have it without spending money.

Fortunately, the shopkeeper enjoyed puzzle game. So he drew a n × m matrix on the paper with integer value ai,j in each cell. He wanted to find 4 numbers x, y, x2, and y2(x ≤ x2, y ≤ y2), so that the sum of values in the sub-matrix from (x, y) to (x2, y2) would be the largest.

To make it more interesting, the shopkeeper ordered YK to change exactly one cell's value into P, then to solve the puzzle game. (That means, YK must change one cell's value into P.)

If YK could come up with the correct answer, the shopkeeper would give the picture to YK as a prize.

YK needed your help to find the maximum sum among all possible choices.

输入

There are multiple test cases.

The first line of each case contains three integers n, m and P. (1 ≤ n, m ≤ 300, -1000 ≤ P ≤ 1000).

Then next n lines, each line contains m integers, which means ai,j (-1000 ≤ ai,j ≤ 1000).

输出

For each test, you should output the maximum sum.

样例输入
3 3 4
-100 4 4
4 -10 4
4 4 4
3 3 -1
-2 -2 -2
-2 -2 -2
-2 -2 -2
样例输出
24
-1

题目大意:


让我们必须修改一个位子上的数变成P,从而求一个最大子矩阵的和。


思路:


问题不难,我们考虑是如何求一个最大子矩阵的和的,我们O(n^2)枚举一个子矩阵的上下界,然后O(m)去Dp出最大连续子序列的和即可。

那么这个过程中,我们可以延伸一下最大连续子序列的Dp,我们设定Dp【i】【2】:

Dp【i】【0】表示到位子i的部分,我们还没有修改过值的最大连续子序列的和。

Dp【i】【1】表示到位子i的部分,我们已经修改过值的最大连续子序列的和。


那么不难写出其状态转移方程:


Dp【i】【0】=max(b【i】,dp【i-1】【0】+b【i】);

Dp【i】【1】=b【i】+p-query(i,L,R)【表示查询第i列,上界从L到下届R查询最小值】;

Dp【i】【1】=max(Dp【i】【1】,Dp【i-1】【1】+b【i】);

Dp【i】【2】=max(Dp【i】【1】,Dp【i-1】【0】+b【i】+p-query(i,L,R));


过程转移一下,我们取最大值即可。

但是题目要求我们必须修改一个位子的值,那么我们再维护一个数组pre【i】【2】,表示我们当前的Dp状态是从哪一初始状态转移而来的。

那么如果有i==m&&pre==1&&L==1&&R==n,那么我们不能取Dp【i】【0】;


注意一下就行了。


Ac代码:

#include<stdio.h>
#include<string.h>
#include<math.h>
#include<iostream>
using namespace std;
int n,m,p;
int a[605][605];
int minn[605][605][17];
int Len[2305];
void ST()
{
    int len=floor(log10(double(n))/log10(double(2)));
    for(int z=1;z<=m;z++)
    {
        for(int j=1;j<=len;j++)
        {
            for(int i=1;i<=n+1-(1<<j);i++)
            {
                minn[z][i][j]=min(minn[z][i][j-1],minn[z][i+(1<<(j-1))][j-1]);
            }
        }
    }
}
int query(int col,int a,int b)
{
    int len=Len[b-a+1];
    return min(minn[col][a][len], minn[col][b-(1<<len)+1][len]);
}
int main()
{
    for(int i=0;i<=1000;i++)
    {
        Len[i]=floor(log10(double(i))/log10(double(2)));
    }
    while(~scanf("%d%d%d",&n,&m,&p))
    {
        int ans=-0x3f3f3f3f;
        for(int i=1;i<=n;i++)
        {
            for(int j=1;j<=m;j++)
            {
                scanf("%d",&a[i][j]);
            }
        }
        for(int j=1;j<=m;j++)
        {
            for(int i=1;i<=n;i++)
            {
                minn[j][i][0]=a[i][j];
            }
        }
        ST();

        for(int i=1;i<=n;i++)
        {
            int b[305];
            memset(b,0,sizeof(b));
            for(int j=i;j<=n;j++)
            {
                for(int k=1;k<=m;k++)
                {
                    b[k]+=a[j][k];
                }
                int dp[305][2];
                int pre[305][2];
                memset(pre,0,sizeof(pre));
                memset(dp,0,sizeof(dp));
                for(int k=1;k<=m;k++)
                {
                    if(dp[k-1][0]+b[k]>b[k])
                    {
                        dp[k][0]=dp[k-1][0]+b[k];
                        pre[k][0]=pre[k-1][0];
                    }
                    else
                    {
                        dp[k][0]=b[k];
                        pre[k][0]=k;
                    }
                    if(k==1)pre[k][0]=k;
                    dp[k][1]=b[k]+p-query(k,i,j);
                    pre[k][1]=k;

                    if(k>1)
                    {
                        if(dp[k-1][1]+b[k]>dp[k][1])
                        {
                            dp[k][1]=dp[k-1][1]+b[k];
                            pre[k][1]=pre[k-1][1];
                        }
                        int temp=dp[k-1][0]+b[k]+p-query(k,i,j);
                        if(temp>dp[k][1])
                        {
                             dp[k][1]=temp;
                             pre[k][1]=pre[k-1][0];
                        }
                    }
                    if(!(i==1&&j==n&&k==m&&pre[k][0]<=1))
                    ans=max(ans,dp[k][0]);
                    ans=max(ans,dp[k][1]);
                }
            }
        }
        printf("%d\n",ans);
    }
}















评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值