核心优化思路是减少循环次数,从能偷到子树内最近的画开始计算,能偷完子树内所有画便结束循环
结构体Tree中的dp[i]表示在该子树内,i时间内,能偷到的画的最多数量
以下为代码,关键变量已注释
通过#5测试点花费的时间:
开启O2优化:36ms
不开启O2优化:116ms
#include<cstdio>
#include<cstring>
using namespace std;
const int Maxn = 10050;
int temp,n;
int max(int x,int y){
return x > y ? x : y;
}
int min(int x,int y){
return x < y ? x : y;
}
struct Tree{
Tree *left = NULL;
Tree *right = NULL;
int dp[Maxn];
int num = 0; //画的数量
int len = 0; //往返走廊的时间
int kind = 0; //判断是否为根节点
int max_t = 0; //偷完子树内所有画所需时间
int min_t = 0; //仅往返子树内最近展览室的时间
int tot = 0; //从出口到该位置仅往返时间
Tree(){
memset(dp,0,sizeof(dp));
}
}tree[205];
Tree* scan(){
int t1,t2;
Tree* t = &tree[temp];
scanf("%d %d",&t1,&t2);
t->len = t1 * 2;
if (t2 == 0){
++temp;t->left = scan();t->left->tot = t->tot + t->len;
++temp;t->right = scan();t->right->tot = t->tot + t->len;
t->kind = 1;
}else{
t->num = t2;
}
return t;
}
void f(Tree *t){
if(t->kind){
int i,j,x,y;
f(t->left);
f(t->right);
t->max_t = t->left->max_t + t->len + t->right->max_t;
if(t->max_t > n - t->tot){
t->max_t = n - t->tot;
}
x = t->left->min_t;
y = t->right->min_t;
t->min_t = min(x,y) + t->len;
for(i = t->min_t + 5;i <= t->max_t;++i){
for(j = max(0,i-t->len-t->right->max_t);j <= i - t->len && j <= t->left->max_t;++j){
t->dp[i] = max(t->dp[i],t->left->dp[j] + t->right->dp[i-t->len-j]);
}
}
}else{
t->max_t = 5*t->num + t->len;
t->min_t = t->len;
int k = 5 + t->len,i,j;
for(i = 1;i <= t->num;++i){
for(j = 0;j < 5;++j){
t->dp[k] = i;
++k;
}
}
}
}
int main(){
scanf("%d",&n);
scan();
f(&tree[0]);
if(tree[0].max_t < n - 1){
printf("%d",tree[0].dp[tree[0].max_t]);
}else{
printf("%d",tree[0].dp[n-1]);
}
scanf("%d",&n);
return 0;
}