Dp_关于最大子矩阵的问题总结

  我们知道子一行也是矩阵,只不过是特殊情况,首先来一道题,hdu 1506 

高度h[i]  我们定义两个数组,分别为向左扩展能到的最左边的下标,和向右扩展能到的最右边的下标。

然后两个数组分别从正方向和反反向扫一遍

#include
#include
using namespace std;
#define LL long long
LL num[100005];
LL l[100005],r[100005];
int main()
{
    int n;
    LL ans;
    for(;scanf("%d",&n),n;)
    {
        for(int i=1;i<=n;++i) scanf("%I64d",&num[i]);
        num[0]=num[n+1]=-1;
        for(int i=1;i<=n;++i) l[i]=r[i]=i;
        //dp,根据之前拓展的l,r再拓展,可以提高效率
        for(int i=1;i<=n;++i)
            //第i个高度拓展,h[j]>=h[i]的最小j
            for(;num[l[i]-1]>=num[i];)
               l[i]=l[l[i]-1];
        for(int i=n;i>=1;--i)
            //第i个高度拓展,h[j]>=h[i]的最大j
            for(;num[r[i]+1]>=num[i];)
               r[i]=r[r[i]+1];
        ans=0;
        for(int i=1;i<=n;++i)
            if(ans<num[i]*(r[i]-l[i]+1))
               ans=num[i]*(r[i]-l[i]+1);
        printf("%I64d\n",ans);
    }
    return 0;
}

现在特殊情况考虑完了以后,我们考虑正常矩阵

hdu 1505

这次定义一个Up数组从上往下扫描,相当于矩阵的压缩,于是每次只要对一行的Up进行处理,相当于就转换成了第一个问题特殊情况:一行的矩阵

#include
#include
#include
#include
#include
using namespace std;
#define oo 0x3f3f3f3f
#define maxn 1010
int Up[maxn][maxn];
int L[maxn], R[maxn];
char map[maxn][maxn];

int main()
{
    int T, n, m, ans;
    char val[5];
    scanf("%d", &T);
    while (T--)
    {
        scanf("%d %d", &n, &m);

        for (int i = 1; i <= m; i++)
            Up[0][i] = 0;

        for (int i = 1; i <= n;i++)
        for (int j = 1; j <= m; j++)
        {
            scanf("%s", &val);            
            if (val[0] == 'F')
                Up[i][j] = Up[i - 1][j] + 1;
            else
                Up[i][j] = 0;
        }
        ans = 0;
        for (int i = 1; i <= n; i++)
        {
            for (int j = 1; j <= m; j++)
                L[j] = R[j] = j;

            Up[i][0] = Up[i][m + 1] = -1;

            for (int j = 2; j <= m; j++)
            while (Up[i][L[j] - 1] >= Up[i][j])
                L[j] = L[L[j] - 1];

            for (int j = m - 1; j >= 1;j--)
            while (Up[i][R[j] + 1] >= Up[i][j])
                R[j] = R[R[j] + 1];
            
            for (int j = 1; j <= m; j++)
                ans = max(ans, Up[i][j] * (R[j] - L[j] + 1));

            
        }
        printf("%d\n", ans * 3);
    }
    return 0;
}
/*
1.00
3.71
0.04
5.19
0.00
*/
在以上基础加条件的

hdu 2870 题目加点要求就是允许将某个字母变成某个字母

这样只要将字母变成对应的字母,然后对每种变换进行一次dp,找到其中最大的值,其实情况只要3三种就ok了,贪心思想:尽量让所有的字母变成a,b,c这样只要对a,b,c的情况dp就ok了。

#include
#include
#include
#include
#include
using namespace std;
#define oo 0x3f3f3f3f
#define maxn 1010
int Up[maxn][maxn];
int L[maxn], R[maxn];
char map[maxn][maxn];
bool ok[4][30];

void Solve()
{
    memset(ok, 0, sizeof ok);
    ok[0]['a' - 'a'] = 1;
    ok[0]['w' - 'a'] = 1;
    ok[0]['y' - 'a'] = 1;
    ok[0]['z' - 'a'] = 1;

    ok[1]['w' - 'a'] = 1;
    ok[1]['x' - 'a'] = 1;
    ok[1]['z' - 'a'] = 1;
    ok[1]['b' - 'a'] = 1;

    ok[2]['x' - 'a'] = 1;
    ok[2]['y' - 'a'] = 1;
    ok[2]['z' - 'a'] = 1;
    ok[2]['c' - 'a'] = 1;

}

int main()
{
    Solve();
    int n, m, ans;
    while (scanf("%d %d", &n, &m) != EOF)
    {
        for (int i = 1; i <= m; i++)
            Up[0][i] = 0;

        for (int i = 1; i <= n; i++)
            scanf("%s", map[i] + 1);

        ans = 0;
        for (int k = 0; k < 3; k++)
        {
            for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
            {
                if (ok[k][map[i][j] - 'a'])
                    Up[i][j] = Up[i - 1][j] + 1;
                else
                    Up[i][j] = 0;
            }
            
            for (int i = 1; i <= n; i++)
            {
                for (int j = 1; j <= m; j++)
                    L[j] = R[j] = j;

                Up[i][0] = Up[i][m + 1] = -1;

                for (int j = 2; j <= m; j++)
                while (Up[i][L[j] - 1] >= Up[i][j])
                    L[j] = L[L[j] - 1];

                for (int j = m - 1; j >= 1; j--)
                while (Up[i][R[j] + 1] >= Up[i][j])
                    R[j] = R[R[j] + 1];

                for (int j = 1; j <= m; j++)
                    ans = max(ans, Up[i][j] * (R[j] - L[j] + 1));
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}
/*
1.00
3.71
0.04
5.19
0.00
*/
现在稍微再改变下

hdu 2830 题目要求可以交换任意列任意次,一开没想到怎么做,看了题解发现,只要对每次的行进行列的排序,然后枚举列大到小的长度,这样就可以找出最大的值。

#include
#include
#include
#include
#include
using namespace std;
#define oo 0x3f3f3f3f
#define maxn 1010
int up[2][maxn];
int res[maxn];
char map[2][maxn];

int main()
{
    int n, m, ans;
    while (scanf("%d %d", &n, &m) != EOF)
    {
        memset(up, 0, sizeof up);
        ans = 0;
        for (int i = 1; i <= n; i++)
        {
            scanf("%s", map[i % 2] + 1);
            
            for (int j = 1; j <= m; j++)
            {
                if (map[i % 2][j] == '1')
                    up[i % 2][j] = up[(i - 1) % 2][j] + 1;
                else
                    up[i % 2][j] = 0;
            }

            memcpy(res, up[i % 2], sizeof res);
            sort(res + 1, res + 1 + m);

            for (int j = 1; j <= m; j++)
                ans = max(ans, (m - j + 1)*res[j]);
        }
        printf("%d\n", ans);
    }
    return 0;
}
/*
1.00
3.71
0.04
5.19
0.00
*/

你可能感兴趣的:(动态规划)