openjudge动规刷题攻略----第一弹

来源:互联网 发布:newsql数据库有哪些 编辑:程序博客网 时间:2024/06/06 15:36

基本算法之动态规划(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];

   }

 

 

0 0
原创粉丝点击