动态规划求最大子矩阵详解(hdu 1505,1506 , 2870)

来源:互联网 发布:提高淘宝店铺关注率 编辑:程序博客网 时间:2024/06/08 14:13

动态规划求最大子矩阵

对于hdu 1505,1506,都是求关于最大子矩阵的问题,因为n为1000,n^3必然超时,因此需要优化,这类题应用了一种特殊的优化算法使时间复杂度降到了n*n。

优化算法

因为求最大子矩阵,都需要求最左边比该点高的位置,最右边比该点高的位置。因此就先假如我们现在我们求l[n],很明显现在l[1……n-1]都已经求出来了,那么我们就可以利用前面求得的对其进行优化,同理求r[n]。

优化的代码:
l[1] = 1;
for(int i = 2; i <= n; i++)
{
int t = i;
while(t > 1 && h[t-1] >= h[i])
t = l[t-1];
l[i] = t;
}
r[n] = n;
for(int i = n-1; i >= 1; i–)
{
int t = i;
while(t < n && h[t+1] >= h[i])
t = r[t+1];
r[i] = t;
}

因此1506题就可以得出,代码为:

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;typedef long long LL;const int maxn = 100005;LL l[maxn],r[maxn],h[maxn];int main(){    int n;    while(scanf("%d", &n) != EOF && n)    {        for(int i = 1; i <= n; i++)            scanf("%I64d", &h[i]);        l[1] = 1;        for(int i = 2; i <= n; i++)        {            int t = i;            while(t > 1 && h[t-1] >= h[i])                t = l[t-1];            l[i] = t;        }        r[n] = n;        for(int i = n-1; i >= 1; i--)        {            int t = i;            while(t < n && h[t+1] >= h[i])                t = r[t+1];            r[i] = t;        }        LL ans = 0;        for(int i = 1; i <= n; i++)            ans = max(ans, h[i]*(r[i]-l[i]+1));        printf("%I64d\n", ans);    }    return 0;}

对于1505则需要转化一下:对于F(空闲区),我们将该值设置为dp[i][j] = dp[i-1][j] +1;对于R直接设置为0即可。举个例子:

R F F R
F F F F
F F F R
R F F F

0 1 1 0
1 2 2 1
2 3 3 0
0 4 4 1
这样同样可以用1506题的解法解决这一问题,AC代码为:

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int maxn = 1005;int dp[maxn][maxn];int l[maxn],r[maxn];int main(){    char s[2];    int T,n,m;    scanf("%d", &T);    while(T--)    {        scanf("%d %d", &n, &m);        memset(dp, 0, sizeof(dp));        for(int i = 1; i <= n; i++)        {            for(int j = 1; j <= m; j++)            {                scanf("%s", s);                if(s[0] == 'F') dp[i][j] = dp[i-1][j] + 1;                else dp[i][j] = 0;            }        }        int ans = 0;        for(int i = 1; i <= n; i++)        {            l[1] = 1;            for(int j = 2; j <= m; j++)            {                int t = j;                while(t > 1 && dp[i][t-1] >= dp[i][j])                    t = l[t-1];                l[j] = t;            }            r[m] = m;            for(int j = m-1; j >= 1; j--)            {                int t = j;                while(t < m && dp[i][t+1] >= dp[i][j])                    t = r[t+1];                r[j] = t;            }            for(int j = 1; j <= m; j++)            ans = max(ans, (r[j]-l[j]+1)*dp[i][j]);        }        printf("%d\n", ans*3);    }    return 0;}

对于2870题,很明显也是求最大子矩阵问题,但是这里面有3种字母,很明显将别的字母统一转化a,b,c即可求出,剩余就和1505题一样了。给出AC代码:

#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>using namespace std;const int maxn = 1005;char s[maxn][maxn];int dp[maxn][maxn],l[maxn],r[maxn];int main(){    int n,m;    while(scanf("%d %d", &n, &m) != EOF)    {        for(int i = 1; i <= n; i++)            scanf("%s", s[i] + 1);        int ans = 0;        memset(dp,0,sizeof(dp));        for(int i = 1; i <= n; i++)            for(int j = 1; j <= m; j++)                if(s[i][j] == 'a' || s[i][j] == 'w' || s[i][j] == 'y' || s[i][j] == 'z')                    dp[i][j] = dp[i-1][j] + 1;                else dp[i][j] = 0;        for(int i = 1; i <= n; i++)        {            l[1] = 1;            for(int j = 2; j <= m; j++)            {                int t = j;                while(t > 1 && dp[i][t-1] >= dp[i][j])                    t = l[t-1];                l[j] = t;            }            r[m] = m;            for(int j = m-1; j >= 1; j--)            {                int t = j;                while(t < m && dp[i][t+1] >= dp[i][j])                    t = r[t+1];                r[j] = t;            }            for(int j = 1; j <= m; j++)                ans = max(ans, (r[j]-l[j]+1)*dp[i][j]);        }        memset(dp,0,sizeof(dp));        for(int i = 1; i <= n; i++)            for(int j = 1; j <= m; j++)                if(s[i][j] == 'b' || s[i][j] == 'w' || s[i][j] == 'x' || s[i][j] == 'z')                    dp[i][j] = dp[i-1][j] + 1;                else dp[i][j] = 0;        for(int i = 1; i <= n; i++)        {            l[1] = 1;            for(int j = 2; j <= m; j++)            {                int t = j;                while(t > 1 && dp[i][t-1] >= dp[i][j])                    t = l[t-1];                l[j] = t;            }            r[m] = m;            for(int j = m-1; j >= 1; j--)            {                int t = j;                while(t < m && dp[i][t+1] >= dp[i][j])                    t = r[t+1];                r[j] = t;            }            for(int j = 1; j <= m; j++)                ans = max(ans, (r[j]-l[j]+1)*dp[i][j]);        }        memset(dp,0,sizeof(dp));        for(int i = 1; i <= n; i++)            for(int j = 1; j <= m; j++)                if(s[i][j] == 'c' || s[i][j] == 'x' || s[i][j] == 'y' || s[i][j] == 'z')                    dp[i][j] = dp[i-1][j] + 1;                else dp[i][j] = 0;        for(int i = 1; i <= n; i++)        {            l[1] = 1;            for(int j = 2; j <= m; j++)            {                int t = j;                while(t > 1 && dp[i][t-1] >= dp[i][j])                    t = l[t-1];                l[j] = t;            }            r[m] = m;            for(int j = m-1; j >= 1; j--)            {                int t = j;                while(t < m && dp[i][t+1] >= dp[i][j])                    t = r[t+1];                r[j] = t;            }            for(int j = 1; j <= m; j++)                ans = max(ans, (r[j]-l[j]+1)*dp[i][j]);        }        printf("%d\n", ans);    }    return 0;}
0 0
原创粉丝点击