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\n", 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\n", 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\n",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\n",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\n", 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

posted @ 2017-08-21 16:59  ╰追憶似水年華ぃ╮  阅读(188)  评论(0编辑  收藏  举报