您的位置:首页 > 其它

《现代程序设计》9.9日课后总结

2013-09-10 14:57 260 查看
邹老师的第一门课上小测了最大子矩阵的问题,课堂上只想到了O(n^2*m^2)的算法

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

一.最大子段和

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

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=100005;
int map[maxn];
int ans;
int begin;
int end;
int n;
void getmap()
{
for(int i=1;i<=n;i++)
{
cin>>map[i];
}
}
void work()
{
int tempsum=0;
int tmpbegin=1;
for(int i=1;i<=n;i++)
{
tempsum+=map[i];
if(tempsum>ans)
{
ans=tempsum;
begin=tmpbegin;
end=i;
}
if(tempsum<0)
{
tempsum=0;
tmpbegin=i+1;
}

}
}

int main()
{
int T;
cin>>T;
for(int tt=1;tt<=T;tt++)
{
ans=-INF;
cin>>n;
getmap();
work();
cout<<"Case "<<tt<<":"<<endl;
cout<<ans<<" "<<begin<<" "<<end<<endl;
if(tt<T)
{
cout<<endl;
}
}
return 0;
}


关于最大子段和有一种变形是最大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)

/*
dp[i][j]前j个数(包括第j个数,组成i组的和的最大值
dp[i][j]=max(dp[i-1][j-1]+map[j],dp[i-1][k]+map[j]),0<k<j
即根据第j个数是否单独成组划分状态
前i组仅与前i-1组有关,这里用一维数组就好
而dp[i-1][k]其实就是上一组中从0-j-1中划分为i-1组的最大值
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=1000005;
__int64 map[maxn];
__int64 dp[maxn];
__int64 prev[maxn];
__int64 ans;
int m;
int n;
void getmap()
{
dp[0]=0;
prev[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%I64d",&map[i]);
dp[i]=0;
prev[i]=0;
}
}
void work()
{
for(int i=1;i<=m;i++)
{
ans=-INF;
for(int j=i;j<=n;j++)
{
dp[j]=max(dp[j-1],prev[j-1])+map[j];
prev[j-1]=ans;
ans=max(ans,dp[j]);
}
}
}
int main()
{
while(scanf("%d%d",&m,&n)!=EOF)
{
getmap();
work();
printf("%I64d\n",ans);
}
return 0;
}


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

/*
dp[i][j]前j个数(包括第j个数,组成i组的和的最大值
dp[i][j]=max(dp[i-1][j-1]+map[j],dp[i-1][k]+map[j]),0<k<j
即根据第j个数是否单独成组划分状态
前i组仅与前i-1组有关,这里用一维数组就好
而dp[i-1][k]其实就是上一组中从0-j-1中划分为i-1组的最大值
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=1000005;
int map[maxn];
int dp[maxn];
int prev[maxn];
int ans;
int m;
int n;
void getmap()
{
dp[0]=0;
prev[0]=0;
for(int i=1;i<=n;i++)
{
scanf("%d",&map[i]);
dp[i]=0;
prev[i]=0;
}
}
void work()
{
for(int i=1;i<=m;i++)
{
ans=-INF;
for(int j=i;j<=n;j++)
{
dp[j]=max(dp[j-1],prev[j-1])+map[j];
prev[j-1]=ans;
ans=max(ans,dp[j]);
}
}
}
int main()
{
while(scanf("%d",&n)!=EOF&&n)
{
m=2;
getmap();
work();
printf("%d\n",ans);
}
return 0;
}


二.最大子矩阵

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

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

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#define INF 0x3f3f3f3f
using namespace std;
const int maxn=105;
int map[maxn][maxn];
int sum[maxn][maxn];
int ans;
int n;
void getmap()
{
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
cin>>map[i][j];
}
}
}
void getsum()
{
memset(sum,0,sizeof(sum));
for(int i=1;i<=n;i++)
{
for(int j=1;j<=n;j++)
{
sum[i][j]=sum[i-1][j]+sum[i][j-1]-sum[i-1][j-1]+map[i][j];
}
}
}
void work()
{
ans=-INF;
for(int i=1;i<=n;i++)
{
for(int j=i;j<=n;j++)
{
int start=sum[j]
-sum[i-1]
-sum[j][n-1]+sum[i-1][n-1];
int all=start;
for(int k=n-1;k>=1;k--)
{
if(start<0)
{
start=0;
}
start+=sum[j][k]-sum[i-1][k]-sum[j][k-1]+sum[i-1][k-1];
all=max(all,start);
}
ans=max(ans,all);
}
}
}
int main()
{
while(cin>>n)
{
getmap();
getsum();
work();
cout<<ans<<endl;
}
return 0;
}


暂时只能想到这些,以后遇到该问题的变形,比如像课后扩展那样对于矩阵可以首尾相连,二维扩展到三维、四维等等的问题,我会继续在这里更新。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: