基本算法之动态规划(1~4)
1481 Maximumsum
题目大意:求两个最大连续子序列和的和。
解题思路
搞起来很玄妙,正序、倒序分别来一遍DP:
sum_1[i]表示前i个数里的最大连续子序列和,sum_2[i]表示后i个数里的最大连续子序列和。至于为啥这样跑……反正我是想不出更好的方法了。t1存的是包含当前数a[i]的最大序列和,而t2存的是sum_1[i],每次先更新t1再更新t2。
参考代码
int t1=-21000,t2=0;
for (int i=1;i<=n;i++)
{
if (t2>0) t2+=a[i];
else t2=a[i];
t1=max(t1,t2);
sum_1[i]=t1;
}
t1=-210000,t2=0;
for (int i=n;i;i--)
{
if (t2>0) t2+=a[i];
else t2=a[i];
t1=max(t1,t2);
sum_2[i]=t1;
}
int ans=sum_1[1]+sum_2[2];
for (int i=2;i<n;i++)
if (sum_1[i]+sum_2[i+1]>ans)
ans=sum_1[i]+sum_2[i+1];
162 Post Office
题目大意:给定一个整数n和一个整数k,给出n个位置坐标,在这n个位置里选k个位置作为邮局,使这n个位置到最近的邮局的距离和最小。
解题思路
先预处理出每个子区间内建一个邮局的最小距离和(贪心,取最中间的点作为邮局),然后跑DP:f[i][k]表示前i个点建k个邮局的最小距离和,枚举断点j(i<=j<i),将j~i部分看作一个子区间,建一个邮局,很容易得到转移方程:
f[i][k]=min(f[j][k-1]+s[j+1][i])(1<=j<i)
参考代码
for (int i=1;i<=n;i++)s[i][i]=0;
for(int i=1;i<n;i++)
for (int j=i+1;j<=n;j++)
{
int mid=pos[(i+j)/2];
int sum=0;
for (int k=i;k<=j;k++)
sum+=abs(mid-pos[k]);
s[i][j]=sum;
}
memset(f,0x3f,sizeof(f));
for (int i=1;i<=n;i++)
f[i][1]=s[1][i];
for (int k=2;k<=kk;k++)
{
for (int i=k;i<=n;i++)
for (int j=1;j<i;j++)
if (j>=k-1)
f[i][k]=min(f[i][k],f[j][k-1]+s[j+1][i]);
}
1759最长上升子序列
解题思路
贴一个nlogn做法的代码,详情参考神犇博客:
http://blog.csdn.net/wall_f/article/details/8295812
参考代码
int s[1005],a[1005];
int ef(int l,int r,int st)
{
while (l<=r)
{
int mid=(l+r)/2;
if (s[mid]<st) l=mid+1;
else r=mid-1;
}
return l;
}
int main()
{
memset(s,0x3f,sizeof(s));
//s[i]表示长度为i的序列末尾数字的最小值;
int n;
cin>>n;
for (int i=1;i<=n;i++)
cin>>a[i];
int ans=0;
for (int i=1;i<=n;i++)
{
int pos=ef(1,i,a[i]);
//返回末位数字恰好比a[i]大的序列长度;
s[pos]=min(s[pos],a[i]);
ans=max(ans,pos);
//更新最大序列长度;
}
cout<<ans;
return 0;
}
1786 最大子矩阵
解题思路
四重循环现在都不敢写了……f[i][j]表示以(i,j)为右下角的矩阵里的最大子矩阵,但预处理只能求出(0,0)为左上角的情况,所以需要枚举一个点(k,l)。下图中蓝+灰、紫+灰的部分是可以枚举的,而且一定在之前处理过,我们的目标是求右下角的那一小块的和,然后和f[i][j]比较取较大值,然后和ans取较大值作为结果。预处理的时候其实也有DP的思想。
参考代码
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
{
cin>>a[i][j];
s[i][j]=s[i-1][j]+s[i][j-1]-s[i-1][j-1]+a[i][j];
}
memset(f,0,sizeof(f));
int ans=0;
for (int i=1;i<=n;i++)
for (int j=1;j<=n;j++)
for (int k=0;k<i;k++)
for (int l=0;l<j;l++)
{
f[i][j]=max(f[i][j],s[i][j]-s[i][l]-s[k][j]+s[k][l]);
if (f[i][j]>ans) ans=f[i][j];
}