1.NYOJ44-最大连续子串和
两种解法:
1.归并:(运行时间比DP长O(n*lgn),但是耗用内存小,好吧,其实dp不用开很大内存,O(1)的dp也可以解决,总体来说,dp更好一些)
无非三种情况,最大连续子串在mid左边,最大连续子串在mid右边,最大连续子串横跨左边和右边
递归调用即可
Code:
#include"iostream"
#include"cstdio"
#include"cstring"
#include"cstdlib"
#define N 1000005
using namespace std;
int t;
int n;
int data[N];
int sum;
int mergesort(int left,int mid,int right)
{
int leftsum=0;
int rightsum=0;
int sum=0;
for(int i=mid;i>=left;i--)
{
sum+=data[i];
leftsum=max(leftsum,sum);
}
sum=0;
for(int i=mid+1;i<=right;i++)
{
sum+=data[i];
rightsum=max(rightsum,sum);
}
return leftsum+rightsum;
}
int merge(int left,int right)
{
int leftsum=0;
int rightsum=0;
int crosssum=0;
int mid=(left+right)/2;
if(left==right) return data[left];
leftsum=merge(left,mid);
rightsum=merge(mid+1,right);
crosssum=mergesort(left,mid,right);
if(leftsum>=rightsum&&leftsum>=crosssum) return leftsum;
else if(rightsum>=leftsum&&rightsum>=crosssum) return rightsum;
else return crosssum;
}
int main()
{
scanf("%d",&t);
while(t--)
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&data[i]);
printf("%d\n",merge(1,n));
}
return 0;
}
2.DP:
定义状态:
dp[i]表示以i结尾的最大连续子串的和
状态转移方程:
if dp[i-1] < 0 dp[i]=data[i] //含义是,如果前面的最大连续子串和是负的,我们没必要让负值拉低我们的最大值,我们直接选取当前的元素即可
else dp[i]=dp[i-1]+data[i]
Code:
#include"iostream"
#include"cstdio"
#include"cstring"
#include"cstdlib"
#define N 1000005
using namespace std;
int n;
int data[N];
int dp[N];
int main()
{
int t;
scanf("%d",&t);
while(t--)
{
int m=-99999999;
dp[0]=0;
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&data[i]);
for(int i=1;i<=n;i++)
{
if(dp[i-1]>0) dp[i]=dp[i-1]+data[i];
else dp[i]=data[i];
m=max(m,dp[i]);
}
printf("%d\n",m);
}
return 0;
}
这里我们可以优化一下,没必要开那么大的内存空间,我们直接记录前一个的数据就可以O(1)的空间复杂度
NYOJ 104 矩阵最大子矩阵和
Solution:
本题其实是上述问题的一个升级版本的变体
我们实际上可以将矩阵问题转化成以为问题
我们这样来转化,在这里我们开辟二维数组temp记录一直到每行下的每列的元素的和,那么我们只要用相应的temp减去上面的temp便可以枚举出二维矩阵的行的所有的情况,这时候,我们把行的数值当作上面NYOJ44问题的data域进行最大子列和的求解就可以了
Code:
#include"iostream"
#include"cstdio"
#include"cstring"
#define N 105
using namespace std;
int t;
int n,m;
int data[N][N];
int temp[N][N];
int dp[N];
int main()
{
cin>>t;
while(t--)
{
memset(temp,0,sizeof(temp));
memset(dp,0,sizeof(dp));
int maxp=-99999999;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
cin>>data[i][j];
}
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
temp[i][j]=temp[i-1][j]+data[i][j];
}
}
for(int i=1;i<=n;i++) //外层的两个循环遍历所有的行的情况
{
for(int j=i;j<=n;j++)
{
for(int k=1;k<=m;k++) //求最大连续子段和
{
if(dp[k-1]>0) dp[k]=temp[j][k]-temp[i-1][k]+dp[k-1];
else dp[k]=temp[j][k]-temp[i-1][k];
maxp=max(maxp,dp[k]);
}
}
}
cout<<maxp<<endl;
}
return 0;
}