7.28DP训练总结
时间安排
8:00–8:20 读题
- T1:将每个点的坐标转化为每个点的两个关键量而不在地图上操作,可以直接用线性DP的思路来做,枚举走过的点数,跳过的点数
- T2:需要用这些数字得到 ≤ n \leq n ≤n的所有数字,先想到了2的整数次幂拆分,第一问解决。但用DP求第二问的方案数思路不清晰,就先跳过了
- T3:计算两个点之间的距离,首先想到了换根DP,但是需要花费时间推换根公式,就打算放到最后
8:20–9:20 T1
T1实现起来最简单,所以码出基本框架后花了一个多小时处理细节,刚开始DP枚举状态时,第一层循环枚举走过的点数(即状态),第二层枚举前一个点的位置,方便得到走过的距离。但是样例过了以后,造的样例过不了,发现是第二层循环枚举时不能控制跳过的点数 k k k,于是将第二层循环改成与前一个点相隔的点数,过掉了几个样例
9:20–9:40 T2
用二的整数次幂拆分得到第一问后,第二问用DP还是没思路,尝试结合之前做过的整数划分,但是发现模型压根就不同(一个是得到 n n n,一个是得到 1 − n 1-n 1−n),又发现有30分 n ≤ 10 n\leq 10 n≤10,于是手推打了个表,先骗30分。
9:40–10:00 T3
发现和之前做的换根DP求距离差不多,无非最后在枚举一下寻找满足条件的点对,于是用之前推的公式 d i s [ n o w ] − 2 × s i z [ f a t h e r ] dis[now]-2\times siz[father] dis[now]−2×siz[father],但是过不了样例,最后没有时间再思考了,就简单打了个Floyd求出所有最短距离判断,但由于时间复杂度的问题,肯定连前40分都拿不了
总结
- T1是比较简单的DP,A掉了
- T2只拿了30分的打表分。如果用DP得到方案数会比较难理解,所以采用 d f s + 剪枝 dfs+剪枝 dfs+剪枝也可以过掉, d f s dfs dfs的三个参数分别为目前的整数,已经选用的整数的和,选用的整数个数;边界:当整数个数等于最小个数时,判断是否 ≥ n ≥n ≥n。剪枝:二的整次幂不断上升的和仍小于 n n n时,退出 d f s dfs dfs。
- T3:将树的根分为两部分, f [ i ] [ j ] f[i][j] f[i][j]表示到点 i i i距离为 j j j的点集个数,枚举到 n o w now now距离为 k − j k-j k−j的点集个数和到 n o w now now的子节点 e d g e [ i ] . y edge[i].y edge[i].y距离为 j − 1 j-1 j−1的点集个数(减去 n o w now now到 e d g e [ i ] . y edge[i].y edge[i].y的距离),将两部分相乘累加到答案上,然后将这两个处理过的子树合并,与下一棵子树进行状态转移即可。
> T3代码如下:
void dfs(int now,int father)
{
f[now][0]=1;
for(int i=link[now];i;i=edge[i].nxt)
{
if(edge[i].y==father) continue;
dfs(edge[i].y,now);
for(int j=1;j<=k;j++)
{
ans+=f[now][k-j]*f[edge[i].y][j-1];//到now距离为k-j的所有点,到edge[i].y距离为j-1的所有点
}
for(int j=0;j<k;j++)
{
f[now][j+1]+=f[edge[i].y][j];
}
}
}