首页 > 技术文章 > 《现代程序设计》9.9日课后总结

oldoldb 2013-09-10 14:57 原文

邹老师的第一门课上小测了最大子矩阵的问题,课堂上只想到了O(n^2*m^2)的算法

后来翻《编程之美》的时候翻到了这道题...今天下午研究了一下,到OJ上切了几道题,熟悉了动态规划在这个问题中的应用.

一.最大子段和

书上已经将这种思想讲的很透彻,对基本的最大子段和的练习可以参照hdu1003题http://acm.hdu.edu.cn/showproblem.php?pid=1003,唯一的一点变形是这道题要求求出最大字段的起始位置和终止位置.

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<algorithm>
 6 #define INF 0x3f3f3f3f
 7 using namespace std;
 8 const int maxn=100005;
 9 int map[maxn];
10 int ans;
11 int begin;
12 int end;
13 int n;
14 void getmap()
15 {
16     for(int i=1;i<=n;i++)
17     {
18         cin>>map[i];
19     }
20 }
21 void work()
22 {
23     int tempsum=0;
24     int tmpbegin=1;
25     for(int i=1;i<=n;i++)
26     {
27         tempsum+=map[i];
28         if(tempsum>ans)
29         {
30             ans=tempsum;
31             begin=tmpbegin;
32             end=i;
33         }
34         if(tempsum<0)
35         {
36             tempsum=0;
37             tmpbegin=i+1;
38         }
39         
40     }
41 }
42 
43 int main()
44 {
45     int T;
46     cin>>T;
47     for(int tt=1;tt<=T;tt++)
48     {
49         ans=-INF;
50         cin>>n;
51         getmap();
52         work();
53         cout<<"Case "<<tt<<":"<<endl;
54         cout<<ans<<" "<<begin<<" "<<end<<endl;
55         if(tt<T)
56         {
57             cout<<endl;
58         }
59     }
60     return 0;
61 }

 

关于最大子段和有一种变形是最大m子段和,http://acm.hdu.edu.cn/showproblem.php?pid=1024,就是在原有的基础上求m段子段的和,这里建立动态规划方程,dp[i][j]表示前j个数(包括第j个数)组成i段的和的最大值,那么根据第j个数是否独立成组得到转移方程为dp[i][j]=max(dp[i-1][j-1]+a[j],dp[i-1][k]+a[j]),0<k<j,这里dp[i][]只和dp[i-1][]有关,用一维数组就可以表示,同时dp[i-1][k]表示1~j-1中构成i-1组的最大值,即前一组的值,那么可以在循环中求出,所以复杂度为O(n*m)

 1 /*
 2 dp[i][j]前j个数(包括第j个数,组成i组的和的最大值
 3 dp[i][j]=max(dp[i-1][j-1]+map[j],dp[i-1][k]+map[j]),0<k<j
 4 即根据第j个数是否单独成组划分状态
 5 前i组仅与前i-1组有关,这里用一维数组就好
 6 而dp[i-1][k]其实就是上一组中从0-j-1中划分为i-1组的最大值 
 7 */
 8 #include<iostream>
 9 #include<cstdio>
10 #include<cstring>
11 #include<cstdlib>
12 #include<algorithm>
13 #define INF 0x3f3f3f3f
14 using namespace std;
15 const int maxn=1000005;
16 __int64 map[maxn];
17 __int64 dp[maxn];
18 __int64 prev[maxn];
19 __int64 ans;
20 int m;
21 int n;
22 void getmap()
23 {
24     dp[0]=0;
25     prev[0]=0;
26     for(int i=1;i<=n;i++)
27     {
28         scanf("%I64d",&map[i]);
29         dp[i]=0;
30         prev[i]=0;
31     }
32 }
33 void work()
34 {
35     for(int i=1;i<=m;i++)
36     {
37         ans=-INF;
38         for(int j=i;j<=n;j++)
39         {
40             dp[j]=max(dp[j-1],prev[j-1])+map[j];
41             prev[j-1]=ans;
42             ans=max(ans,dp[j]);
43         }
44     }
45 }
46 int main()
47 {
48     while(scanf("%d%d",&m,&n)!=EOF)
49     {
50         getmap();
51         work();
52         printf("%I64d\n",ans);
53     }
54     return 0;
55 }

 

根据这道题那么poj2593,http://poj.org/problem?id=2593,就很容易解决了...就是将m替换为两段就好

 1 /*
 2 dp[i][j]前j个数(包括第j个数,组成i组的和的最大值
 3 dp[i][j]=max(dp[i-1][j-1]+map[j],dp[i-1][k]+map[j]),0<k<j
 4 即根据第j个数是否单独成组划分状态
 5 前i组仅与前i-1组有关,这里用一维数组就好
 6 而dp[i-1][k]其实就是上一组中从0-j-1中划分为i-1组的最大值 
 7 */
 8 #include<iostream>
 9 #include<cstdio>
10 #include<cstring>
11 #include<cstdlib>
12 #include<algorithm>
13 #define INF 0x3f3f3f3f
14 using namespace std;
15 const int maxn=1000005;
16 int map[maxn];
17 int dp[maxn];
18 int prev[maxn];
19 int ans;
20 int m;
21 int n;
22 void getmap()
23 {
24     dp[0]=0;
25     prev[0]=0;
26     for(int i=1;i<=n;i++)
27     {
28         scanf("%d",&map[i]);
29         dp[i]=0;
30         prev[i]=0;
31     }
32 }
33 void work()
34 {
35     for(int i=1;i<=m;i++)
36     {
37         ans=-INF;
38         for(int j=i;j<=n;j++)
39         {
40             dp[j]=max(dp[j-1],prev[j-1])+map[j];
41             prev[j-1]=ans;
42             ans=max(ans,dp[j]);
43         }
44     }
45 }
46 int main()
47 {
48     while(scanf("%d",&n)!=EOF&&n)
49     {
50         m=2;
51         getmap();
52         work();
53         printf("%d\n",ans);
54     }
55     return 0;
56 }

二.最大子矩阵

最大子矩阵的算法就是将第k列中第i行到j行的数的和求出,然后套用一维的算法就好

我拿poj1050,http://poj.org/problem?id=1050,做了练习

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<cstdlib>
 5 #include<algorithm>
 6 #define INF 0x3f3f3f3f
 7 using namespace std;
 8 const int maxn=105;
 9 int map[maxn][maxn];
10 int sum[maxn][maxn];
11 int ans;
12 int n;
13 void getmap()
14 {
15     for(int i=1;i<=n;i++)
16     {
17         for(int j=1;j<=n;j++)
18         {
19             cin>>map[i][j];
20         }
21     }
22 }
23 void getsum()
24 {
25     memset(sum,0,sizeof(sum));
26     for(int i=1;i<=n;i++)
27     {
28         for(int j=1;j<=n;j++)
29         {
30             sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+map[i][j];
31         }
32     }
33 }
34 void work()
35 {
36     ans=-INF;
37     for(int i=1;i<=n;i++)
38     {
39         for(int j=i;j<=n;j++)
40         {
41             int start=sum[j][n]-sum[i-1][n]-sum[j][n-1]+sum[i-1][n-1];
42             int all=start;
43             for(int k=n-1;k>=1;k--)
44             {
45                 if(start<0)
46                 {
47                     start=0;
48                 }
49                 start+=sum[j][k]-sum[i-1][k]-sum[j][k-1]+sum[i-1][k-1];
50                 all=max(all,start);
51             }
52             ans=max(ans,all);
53         }
54     }
55 }
56 int main()
57 {
58     while(cin>>n)
59     {
60         getmap();
61         getsum();
62         work();
63         cout<<ans<<endl;
64     }
65     return 0;
66 }

暂时只能想到这些,以后遇到该问题的变形,比如像课后扩展那样对于矩阵可以首尾相连,二维扩展到三维、四维等等的问题,我会继续在这里更新。

推荐阅读