原题链接:P1270 - “访问”美术馆
叠甲:没AC,还剩最后一个测试用例没过,知道题解区有很多大佬,但是还是想来交流一下,看看我的代码问题在哪。
分析
原题是个很明显的树状dp,需要从房间(即叶结点)开始逐步把最优解递归到根节点;题目输入数据的顺序也按照dfs的顺序,摆明了要用上dfs。
思路
我的思路:将资源(时间)分配的情况进行枚举 + 记忆搜索:
1. 对于有房间的走廊(叶子结点),扣除2*走廊本身所需时间,计算剩下时间足够拿几幅画;
2. 对于每个走廊(父节点),扣除2*走廊本身所需时间,将剩下的时间枚举地分配给尽头的左右走廊(即子节点),记录两个子节点综合起来的最优情况。
我的代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define LL long long
using namespace std;
const int max_n = 16390;
const int max_time = 6000;
int dp[max_n][max_time];// dp[n][i]:第n条走廊上,当限制时间为i时=取得画的数量
int limit;
// 用tree数组记录树形结构的走廊、房间。走廊i的耗时为tree[i]
// 走廊i的分叉走廊为2*i和2*i+1;
// 走廊i尽头若为房间,则tree[2*i]=画的数量,tree[2*i+1]=0。
int tree[max_n] = { 0 };
int dfs(int n, int time_limit)//time_limit:踏进走廊n之前的时限
{
time_limit -= 2 * tree[n];//先走完当前所在的走廊,减去花费的时间,得到真正的时限
//记忆搜索
if (dp[n][time_limit] != 0)
return dp[n][time_limit];
//剪枝
if (time_limit < 5)
return 0;
//若n尽头为房间
if (tree[2 * n + 1] == 0)
{
dp[n][time_limit] = min(time_limit / 5, tree[2 * n]);//拿画的数量
return dp[n][time_limit];
}
//否则 n 是个没搜过的、有分叉口的走廊
int res = 0;//记录最多能拿到的画数量
int time = 0;
int choose = 0;
//枚举自己把当前时限分配给两个岔路的所有情况,得到最优情况。分配本身就是dfs!
for (int i = 0; i <= time_limit; i++)
{
//i为分配给左岔路2*n的时间,time_limit则是给右岔路2*i+1的
int l_res = dfs(2 * n, i);
int r_res = dfs(2 * n + 1, time_limit - i);
if (res < l_res + r_res)
{
res = l_res + r_res;
choose = i;
}
//res = ( (res < l_res + r_res) ? l_res + r_res : res);//记录在有限时间内获得最多画的方案
}
//cout << n<<" choose:"<<choose << endl;
dp[n][time_limit] = res;
return res;
}
int main()
{
cin >> limit;//限时
int time, volume, count=1;
while (cin>>time>>volume)
{
tree[count] = time;
if(volume==0)//走廊i的末端是分叉口
count *= 2;//开始记录走廊2*i的信息
else//这个数不为0说明找到了房间
{
tree[count * 2] = volume;
tree[count * 2 + 1] = 0;
while (count % 2)//奇数则回退到首个有右兄弟节点的父节点
count /= 2;
count++;//若count是偶数则count++(处理右兄弟节点走廊)
}
}
cout << dfs(1, limit-1);
}
自认为可能存在的问题
1. 没有很严格地建个树,选择直接用2*n和2*n+1这样的方式建立父子结点关系;
2. 没有在输入时直接一并处理,而是在输入后重新走一遍
3. 对于dp数组的大小没有好好把控,导致最后一个样例RE了(内存溢出);但是再调大就不礼貌了(?)
但是嘞,在这一天反反复复修改的过程中,我把两次下载测试数据的机会都用掉了……所以今天摆烂!明天下载了再看看到底是什么原因。
也蹲蹲dp大神指点