DP专题考试总结(2)

最近努力学(tui)习(fei)了DP专题,然后就被烤焦了,然后就没有然后了。
对此,我只想说——
DP made me Boom-Sha-Ka-La-Ka

加分二叉树

期望得分:0
实际得分:0
Description
设一个n个节点的二叉树tree的中序遍历为(l,2,3,…,n),其中数字1,2,3,…,n为节点编号。每个节点都有一个分数(均为正整数),记第j个节点的分数为di,tree及它的每个子树都有一个加分,任一棵子树subtree(也包含tree本身)的加分计算方法如下:
subtree的左子树的加分× subtree的右子树的加分+subtree的根的分数
若某个子树为主,规定其加分为1,叶子的加分就是叶节点本身的分数。不考虑它的空
子树。
试求一棵符合中序遍历为(1,2,3,…,n)且加分最高的二叉树tree。要求输出;
(1)tree的最高加分
(2)tree的前序遍历
现在,请你帮助你的好朋友XZ设计一个程序,求得正确的答案。
Data Size
n<=30
分数<=100
思路
考试的时候,看完了题就开始懵了。诶?这么复杂?难道要用什么变态的算法。
然后我就果断地跳了题。后来发现因为数据过水,100分很容易拿,跳了真是可惜了。
有点类似一道区间DP的题目。
这里用f[rt][l][r]表示[l,r]段以rt为根节点的最优加分二叉树,然后记忆化搜索。
感觉整个程序打下来,充斥着暴力的气息,全都是暴力枚举、暴力更新……本人过于蒟蒻,程序毫无技术可言,成功地1ms水了过去。
代码

#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int size=35;
int n,rt[size][size],ans[size][size];
ll tot,score[size],f[size][size][size];
inline ll dfs(int now,int l,int r)
{
    if(f[now][l][r]!=-1)
      return f[now][l][r];
    if(l==r)
    {
        f[now][l][r]=score[l];
        rt[l][r]=l;
        return f[now][l][r];
    }
    ll lc=0,rc=0,t;
    for(int i=l;i//暴力枚举根节点,下同
      if(lc<(t=dfs(i,l,now-1)))
      {
        lc=t;
        rt[l][now-1]=i;
      }
    for(int i=now+1;i<=r;i++)
      if(rc<(t=dfs(i,now+1,r)))
      {
        rc=t;
        rt[now+1][r]=i;
      }
    if(!(l<=now-1))
      lc=1;
    if(!(now+1<=r))
      rc=1;
    f[now][l][r]=lc*rc+score[now];
    return f[now][l][r];
}
inline void print(int l,int r)//按照记录的路径进行前序遍历输出
{
    int now=ans[l][r];
    printf("%d ",now);
    if(l<=now-1)
      print(l,now-1);
    if(now+1<=r)
      print(now+1,r);
}
int main()
{
    memset(f,-1,sizeof(f));
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
      scanf("%lld",&score[i]);
    for(int i=1;i<=n;i++)
    {
        ll t=dfs(i,1,n);
        if(t>tot)//如果搜到了更优的结果就暴力更新记录的路径
        {
            tot=t;
            rt[1][n]=i;
            for(int j=1;j<=n;j++)
              for(int k=j;k<=n;k++)
                ans[j][k]=rt[j][k];
        }
    }
    printf("%lld\n",tot);
    print(1,n);
    return 0;
}

凸多边形的三角剖分

期望得分:100
实际得分:100
Description
给定一具有 N 个顶点(从 1 到 N 编号)的凸多边形,每个顶点的权均已知。问如何把
这个凸多边形划分成N-2个互不相交的三角形,使得这些三角形顶点的权的乘积之和最小?
Date Size
N<50
每个顶点的权值<32768
思路
乍一看题目,似乎是什么特别高深的问题,后来仔细一下,其实是一道区间DP的水题。
f[l][r]就表示对于区间[l,r]的最优答案。
代码

#include 
#include 
using namespace std;
typedef long long ll;
const int size=55;
ll n,a[size],f[size][size];
int main()
{
    scanf("%lld", &n);
    for(int i=1; i<=n; i++)
      scanf("%lld", &a[i]);
    for(int i=0;i<=n;i++)//初始化
    {
        f[i][i]=a[i];
        f[i][i+1]=0;
        for(int j=i+2;j<=n;j++)
          f[i][j]=(ll)32767*32767*32767;
    }
    for(int i=n;i>=1;i--)
      for(int j=i+2;j<=n;j++)
        for(int k=i+1;k<=j-1;k++)//枚举断点
          f[i][j]=min(f[i][j],f[i][k]+f[k][j]+a[i]*a[j]*a[k]);
    printf("%lld\n",f[1][n]);
    return 0;
}

火车进站 (不可做)

期望得分:0
实际得分:0
Description
火车站内往往设有一些主干线分叉出去的铁路支路,供火车停靠,以便上下客或装载货物。铁路支路有一定长度;火车也有一定的长度,且每列火车的长度相等。
假设某东西向的铁路上,有一小站。该站只有一条铁路支路可供火车停靠,并且该铁路支路最多能容纳M 辆火车。为了火车行驶的通畅,该站只允许火车自东方进站,自西方出站,且先进站的火车必须先出站,否则,站内火车将发生堵塞。该火车站工作任务繁忙。每天都有 N 辆自东方驶向西方的火车要求在预定时刻进站,并在站内作一定时间的停靠。
为了满足每辆进站火车的要求,小站的调度工作是井井有条地开展。在小站每天的工作开始前,小站工作人员须阅读所有火车的进站申请,并决定究竞接受哪些火车的申请。而对于不能满足要求的火车,小站必须提前通知它们,请它们改变行车路线,以免影响正常的铁路运输工作。由于火车进站、出站的用时可以忽略不计,小站允许几辆火车同时进站或出站,且小站工作人员可以任意安排这些火车进站的先后排列次序。小站的工作原则是尽量地满足申请火车的要求。
请你编一个程序,帮助工作人员考察某天所有火车的进站申请,计算最多能满足多少火车的要求。
Date Size
N<=100;
M<=3;
思路
这题需要分类讨论,耗时不AC。果断认定不可做,跳题。
状态转移方程:
m=1:f[i]=max(f[j]+1);
m=2:f[i][j]=max(f[k][i]+1);(k

最长前缀 (不可做)

至今未做,据说要用kmp算法,留此坑以后再填……

你可能感兴趣的:(=====动态规划=====)