刷题总结——array(ssoj)

题目:

题目描述

给定 2 个正整数序列 A1, A2,序列长度分别为 L1, L2。
你可以进行以下的一次操作:
1. 选择两个数 K1,K2(1≤K1≤L1, 1≤K2≤L2);
2. 移去 A1 中最后 K1 个数,得到这 K1 个数的和 S1,L1 对应减少 K1;
3. 移去 A2 中最后 K2 个数,得到这 K2 个数的和 S2,L2 对应减少 K2;
此次操作的费用为:(S1-K1) * (S2-K2)。
进行以上操作直至两个序列都为空,求最小的费用总和。
注意:序列为空当且仅当两个序列同时为空。

输入格式

第一行是两个正整数 L1和 L2,表示 A1 与 A2 的长度。
第二行 L1 个整数,表示序列 A1[1..L1]。
第三行 L2 个整数,表示序列 A2[1..L2]。

输出格式

输出一个整数,表示最小费用。

样例数据 1

输入  [复制]

 

 

3 2 
1 2 3 
1 2

输出

2

备注

【样例说明】
第一次选取 K1=1,K2=1。费用为 (3-1)*(2-1) = 2。
第二次选取 K1=2,K2=1。费用为 (1+2-2)*(1-1) = 0。
所以,总费用为 2。

【数据范围】
对 20% 的输入数据:1≤L1,L2≤20
对 40% 的输入数据:1≤L1≤400;1≤L2≤150
对 100% 的输入数据:1≤L1,L2,A1[1..L1],A2[1..L2]≤5,000

题解:

很好的一道dp题·····

首先容易想到,为了消除每次sum-k中k带来的影响,我们可以将所有元素-1,这样每次计算的时候直接sum相乘即可····

然后考虑消除的策略···

打个比方l1=l2=4···我们如果要消除a1[l1]到a1[2]和a2[l2]到a2[3]这两段区间的数的话···最好的策略肯定不是直接一次性消除···而是先消除a1[l1]和a2[l2]这两个数,再消除a1[3]到a1[2]和a2[3]这两段····因此不难发现··每次消除的话a1和a2的区间长度有一个一定为1!

所以我们可以将区间消除转化为要么消除两段末端a1[x],a2[y]中其中一个··要么同时消除两个末端··且此时对答案的贡献为a1[x]*a2[y];

得出dp方程

f[i][j]=min(f[i][j+1],f[i+1][j],f[i+1][j+1])+a1[i+1]*a2[j+1]

其中f[i][j]表示两段分别剩余i,j个数时的最少费用

代码:

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<ctime>
#include<cctype>
#include<cstring>
#include<string>
#include<algorithm>
using namespace std;
const int N=5001;
const int inf=0x3f3f3f3f;
inline int R()
{
  char c;int f=0,i=1;
  for(c=getchar();(c<'0'||c>'9')&&c!='-';c=getchar());
  if(c=='-')  c=getchar(),i=-1;
  for(;c<='9'&&c>='0';c=getchar())  f=(f<<3)+(f<<1)+c-'0';
  return f*i;
}
int l1,l2,a1[N],a2[N];
int f[N][N];
int main()
{
  //freopen("a.in","r",stdin);
  l1=R(),l2=R();
  memset(f,inf,sizeof(f));
  for(int i=1;i<=l1;i++)  a1[i]=R(),a1[i]--;
  for(int i=1;i<=l2;i++)  a2[i]=R(),a2[i]--;
  f[l1-1][l2-1]=a1[l1]*a2[l2];
  for(int i=l1-1;i>=0;i--)
    for(int j=l2-1;j>=0;j--)  
    {  
      if(i==l1-1&&j==l2-1)  continue;
      f[i][j]=min(f[i][j+1],min(f[i+1][j+1],f[i+1][j]))+a1[i+1]*a2[j+1];
    }
  cout<<f[0][0]<<endl;
  return 0;
}

 

posted @ 2017-10-08 19:51  AseanA  阅读(274)  评论(0编辑  收藏  举报