[考试]20150528

// 此博文为迁移而来,写于2015年6月6日,不代表本人现在的观点与看法。原始地址:http://blog.sina.com.cn/s/blog_6022c4720102w3ai.html

 

1、前言
       我也是实在不能忍我的动态规划水平了,所以要好好搞了。那么在狠狠补了许久的初级数据结构以及学了一些新的高级数据结构之后,现在开始把重点放在DP上。
 
2、题目


分析:考虑两种方法。第一种为利用前缀和优化转移,f[i][j]表示从1到 i 选择 j 个元素,即f[i][j]=max(f[k][j−1]+a[i]) (k<i-1)f[i][j]表示从1到 i 选择 j 个元素,且最后一个元素必须选;g[i][j]表示从1到i-1选 j 个元素,即相比f[i][j]少了一个第 i 位。g[i][j]只是起一个辅助作用,意在选第 i 个和不选两种情况下选择最大值。
 
代码:
----------------------------------------------------------------------------------------------------
#include<cstdio>
#define MAXN 1005
#define INF 1<<30
 
typedef long long ll;
 
ll max(ll a,ll b) { return (a>b)?a:b; }
 
ll n,k,f[MAXN][MAXN],g[MAXN][MAXN],a[MAXN];
 
int main()
{
        freopen("a.in","r",stdin);
        freopen("a.out","w",stdout);
        scanf("%I64d %I64d",&n,&k);
        for (int i=1;i<=k;i++) f[0][i]=g[0][i]=-INF;
        for (int i=1;i<=n;i++) scanf("%I64d",&a[i]);
        for (int i=1;i<=n;i++)
                for (int j=1;j<=k;j++)
                {
                        f[i][j]=g[i-1][j-1]+a[i];
                        g[i][j]=max(f[i-1][j],g[i-1][j]);
                }
        printf("%I64d",max(g[n][k],f[n][k]));
----------------------------------------------------------------------------------------------------
注意:需要开long long。
 


分析:灵感来源于最长公共子序列问题,用f[i][j]表示数组a的前i位和数组b的前j位能够连线的条数。根据题意不难发现,线只存在两种情况——一条线单独存在;两条线交叉。即:
           1、a[i]=b[j] 时,f[i][j]=f[i-1][j-1]+1
           2、a[i]≠b[j]时,又存在三种情况:f[i][j]=max(f[x][y]+2,f[i-1][j],f[i][j-1])。上述的x,y分别表示b[j]的数值在数组a的位置,a[i]的数值在数组b的位置。
           x,y 预处理于两个数组中。需注意,必须满足x小于i,y小于j,因为后面的状态你还没算到呢(我想了好久为什么 = =。。)
 
代码:
-----------------------------------------------------------------------------------------------------
#include<cstdio>
#define MAXN 1005
int max(int a,int b) { return (a>b)?a:b; }
int temp[MAXN],link[MAXN],f[MAXN][MAXN],numa[MAXN],numb[MAXN],n,a[MAXN],b[MAXN];
int main()
{
         freopen("b.in","r",stdin);
         freopen("b.out","w",stdout);
         scanf("%d",&n);
         for (int i=1;i<=n;i++) { scanf("%d",&a[i]); numa[a[i]]=i; }
         for (int i=1;i<=n;i++) { scanf("%d",&b[i]); numb[b[i]]=i; }
         for (int i=1;i<=n;i++)
         for (int j=1;j<=n;j++)
         {
                 if (a[i]==b[j]) f[i][j]=f[i-1][j-1]+1;
                 else 
                 {
                         f[i][j]=max(f[i][j-1],f[i-1][j]);
                         if (numa[b[j]]<i && numb[a[i]]<j) f[i][j]=max(max(f[numa[b[j]]][numb[a[i]]]+2,f[i-1][j]),f[i][j-1]);
                 }
        }
       printf("%d",f[n][n]);
       return 0;
}
----------------------------------------------------------------------------------------------------
 
c题跳过。
 


分析:这样题看起来比较麻烦,其实如果巧妙地利用好相对论,写出来的状态转移方程比a题还简单。。。然而这种题最难想出来了。我们假设数组最大值n,放在数组a[i]上。则若n-1在n的前面,则n-1必须为第一位,否则则可以放在任意位置。不然的话,最长上升子序列必定存在长度大于2的情况。根据这条规律,我们可以向前递归——若n-1在n的前面,我们考虑n-2和n的位置关系。若n-2又在n的前面,则n-2只能为第二位,若n-2在n的后面,则任意位置都行;若n-1在n的后面,我们考虑n-2和n-1的位置关系。。等等,有点扯不清了,等下再补充。
 
P.S. 本页两个bug:这套试题是2015年5月28日考的;第二题输入格式和数据范围都有点小问题但并不重要。
原文地址:https://www.cnblogs.com/jinkun113/p/4682869.html