zoukankan      html  css  js  c++  java
  • HDU 1081 To The Max【dp,思维】

    HDU 1081

    题意:给定二维矩阵,求数组的子矩阵的元素和最大是多少。

    题解:这个相当于求最大连续子序列和的加强版,把一维变成了二维。

    先看看一维怎么办的:

     1 int getsum()
     2 {
     3     int tot=0;
     4     int ans=-1e9;
     5     for(int i=1;i<=n;i++){
     6         if(tot<0) tot=0;
     7         tot+=a[i];
     8         if(tot>ans)
     9             ans=tot;
    10     }
    11     return ans;
    12 }

    这种做法太棒了!短短几行,就能解决最大子序列和这个问题。其实这几行代码值得深思。而且这是个在线算法,输入数据及时能给出结果,感觉不能归于动归解法。

      但当求解这道题时,就不知道怎么办了。我当时受到以前做的一道关于求01矩阵最大0子矩阵面积的题的启发,想先预处理每行得到前缀和数组,然后再想办法dp。可是dp状态和方程一直找不好。后来看到别人的解法才明白,他们其实也是预先处理的每行的前缀和。他们的想法就是先求出每行的前缀和数组sum[][],然后依次枚举前k行的第i列到第j列的和,求最大值。感觉就像是枚举出了每一个子矩阵,像暴力>_<.

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int MAXN = 107;
    int sum[MAXN][MAXN];
    
    int main()
    {
        int N, tmp;
        while (scanf("%d", &N) == 1)
        {
            memset(sum, 0, sizeof(sum));
            for (int i = 1; i <= N; i++)
                for (int j = 1; j <= N; j++)
                {
                    scanf(" %d", &tmp);
                    sum[i][j] = sum[i][j - 1] + tmp;//预处理得到每行的前缀和
                }
            int ans = -1e9;
            for (int i = 1; i <= N; i++) {//从第i列
                for (int j = i; j <= N; j++) {//到第j列
                    int tot = 0;
                    for (int k = 1; k <= N; k++)//前k行
                    {
                        if (tot < 0) tot = 0;
                        tot += sum[k][j] - sum[k][i - 1];//第i列到第j列的和
                        if (ans < tot)
                            ans = tot;
                    }
                }
            }
            printf("%d
    ", ans);
        }
        return 0;
    }

    当然,可以前k行第i列到第j列就可以前k列第i行到第j行,其实是一样的:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 const int MAXN = 107;
     7 int sum[MAXN][MAXN];
     8 
     9 int main()
    10 {
    11     int N,tmp;
    12     while (scanf("%d",&N)==1)
    13     {
    14         memset(sum, 0, sizeof(sum));
    15         for(int i=1;i<=N;i++)
    16             for (int j = 1; j <= N; j++)
    17             {
    18                 scanf(" %d", &tmp);
    19                 sum[i][j] = sum[i-1][j] + tmp;
    20             }
    21         int ans = -1e9;
    22         for (int i = 1; i <= N; i++) {
    23             for (int j = i; j <= N; j++) {
    24                 int tot = 0;
    25                 for (int k = 1; k <= N; k++)
    26                 {
    27                     if (tot < 0) tot = 0;
    28                     tot += sum[j][k] - sum[i-1][k];
    29                     if (ans < tot)
    30                         ans = tot;
    31                 } 
    32             }
    33         }
    34         printf("%d
    ", ans);
    35     }
    36     return 0;
    37 }
    按前k列想

    也有人按照矩阵压缩的想法写,我觉得实际上还是上面的做法,而且上面的想法更容易理解。因为这里所谓矩阵压缩也就是几行加在一起求最大连续子序列。虽殊途同归,但毕竟想法不一样,代码上就会稍微有点差别:

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #include<algorithm>
     5 using namespace std;
     6 const int MAXN=105;
     7 int a[MAXN][MAXN],tmp[MAXN];
     8 int N;
     9 
    10 int getsum()
    11 {
    12     int tot=0,mx=0;
    13     for(int i=1;i<=N;i++){
    14         tot+=tmp[i];
    15         if(tot>mx) mx=tot;
    16         if(tot<0) tot=0;
    17     }
    18     return mx;
    19 }
    20 
    21 int main()
    22 {
    23     while(scanf("%d",&N)==1)
    24     {
    25         int temp;
    26         for(int i=1;i<=N;i++)
    27             for(int j=1;j<=N;j++)
    28             scanf("%d",&a[i][j]);
    29         int ans=-1e9;
    30         for(int i=1;i<=N;i++)
    31         {
    32             memset(tmp,0,sizeof(tmp));
    33             for(int j=i;j<=N;j++)
    34             {
    35                 for(int k=1;k<=N;k++)
    36                     tmp[k]+=a[j][k];
    37                 int tot=getsum();
    38                 if(ans<tot)
    39                     ans=tot;
    40             }
    41         }
    42         printf("%d
    ",ans);
    43     }
    44 
    45     return 0;
    46 }
    按矩阵压缩的想法

    还有人用树状数组写,我觉的也很棒。没想到我第一次用到二维树状数组是再做dp题的时候。。。虽然这是伪装成二维树状数组的,因为只更新了一维。按照我的理解二维树状数组就是存了若干个一维树状数组的和。

     1 #include<iostream>
     2 #include<cstring>
     3 #include<cstdio>
     4 #include<algorithm>
     5 using namespace std;
     6 const int MAXN=105;
     7 int sum[MAXN][MAXN]={0};
     8 int N;
     9 
    10 int Lowbit(int x)
    11 {
    12     return x&-x;
    13 }
    14 
    15 void Add(int i,int j,int val)
    16 {
    17     while(j<=N){
    18         sum[i][j]+=val;
    19         j+=Lowbit(j);
    20     }
    21     return;
    22 }
    23 
    24 int Sum(int k,int x)
    25 {
    26     int cnt=0;
    27     while(x>0){
    28         cnt+=sum[k][x];
    29         x-=Lowbit(x);
    30     }
    31     return cnt;
    32 }
    33 
    34 int main()
    35 {
    36     while(scanf("%d",&N)==1)
    37     {
    38         int tmp;
    39         memset(sum,0,sizeof(sum));
    40         for(int i=1;i<=N;i++)
    41         for(int j=1;j<=N;j++){
    42             scanf("%d",&tmp);
    43             Add(i,j,tmp);
    44         }
    45         int ans=-1e9;
    46         for(int i=1;i<=N;i++)
    47         {
    48             for(int j=i;j<=N;j++){
    49                 int tot=0;
    50                 for(int k=1;k<=N;k++){
    51                     if(tot<0) tot=0;
    52                     tot+=Sum(k,j)-Sum(k,i-1);
    53                     if(ans<tot)
    54                         ans=tot;
    55                 }
    56             }
    57         }
    58         printf("%d
    ",ans);
    59     }
    60 
    61     return 0;
    62 }

    当然,加个dp数组看起来更像是dp题

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 using namespace std;
     6 const int MAXN = 107;
     7 int sum[MAXN][MAXN];
     8 int dp[MAXN];
     9 int N;
    10 
    11 int lowbit(int x)
    12 {
    13     return x&-x;
    14 }
    15 
    16 void add(int i, int j, int d)
    17 {
    18     while (j <= N)
    19     {
    20         sum[i][j] += d;
    21         j += lowbit(j);
    22     }
    23     return;
    24 }
    25 
    26 int Sum(int k, int x)
    27 {
    28     int cnt = 0;
    29     while (x>0)
    30     {
    31         cnt += sum[k][x];
    32         x -= lowbit(x);
    33     }
    34     return cnt;
    35 }
    36 
    37 int main()
    38 {
    39     int tmp;
    40     while (scanf("%d",&N)==1)
    41     {
    42         memset(sum, 0, sizeof(sum));
    43         for(int i=1;i<=N;i++)
    44             for (int j = 1; j <= N; j++)
    45             {
    46                 scanf(" %d", &tmp);
    47                 add(i, j, tmp);
    48             }
    49         int ans = -1e9;
    50         for (int i = 1; i <= N; i++) {
    51             for (int j = i; j <= N; j++) {
    52                 memset(dp, 0, sizeof(dp));
    53                 for (int k = 1; k <= N; k++) {
    54                     tmp = Sum(k, j) - Sum(k, i-1);
    55                     if (dp[k - 1] > 0)
    56                         dp[k] = dp[k - 1] + tmp;
    57                     else
    58                         dp[k] = tmp;
    59                     ans = max(ans, dp[k]);
    60                 }
    61             }
    62         }
    63         printf("%d
    ", ans);
    64     }
    65     return 0;
    66 }
    加个dp[]数组

     参考博客(感谢~):

    【1】:http://blog.csdn.net/acmman/article/details/38580931?spm=5176.100239.blogcont18950.3.TbcH0h

    【2】:http://www.cnblogs.com/cenariusxz/p/4309627.html

    【3】:http://www.cnblogs.com/gaigai/archive/2012/03/04/2379728.html

  • 相关阅读:
    VBA中的ColorIndex信息
    登录测试页面
    HttpHandler HttpModule入门篇
    vs 2005的条件断点(调试多线程必会)
    VBA中操作Excel的部分方法代码示例
    c# 线程同步: 详解lock,monitor,同步事件和等待句柄以及mutex
    一个对Entity Framework数据层的封装
    中华人民共和国 第二代身份证 号码规则
    什么是.NET应用程序域
    VBA编程常用语句(转载)
  • 原文地址:https://www.cnblogs.com/zxhyxiao/p/7405409.html
Copyright © 2011-2022 走看看