您的位置:首页 > 其它

DP专题考试总结(2)

2017-10-02 15:52 155 查看
最近努力学(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 <iostream>
#include <cstring>
#include <cstdio>
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<now;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]
=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 <iostream>
#include <cstdio>
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]
);
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算法,留此坑以后再填……
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: