第六届蓝桥杯校园选拔赛试题---派遣敢死队 解题报告
2015-04-18 15:49
288 查看
原题 :
G将军有一支训练有素的军队,这个军队除开G将军外,每名士兵都有一个直接上级(可能是其他士兵,也可能是G将军)。现在G将军将接受一个特别的任务,需要派遣一部分士兵(至少一个)组成一个敢死队,为了增加敢死队队员的独立性,要求如果一名士兵在敢死队中,他的直接上级不能在敢死队中。
请问,G将军有多少种派出敢死队的方法。注意,G将军也可以作为一个士兵进入敢死队。
输入格式
输入的第一行包含一个整数n,表示包括G将军在内的军队的人数。军队的士兵从1至n编号,G将军编号为1。
接下来n-1个数,分别表示编号为2, 3, ..., n的士兵的直接上级编号,编号i的士兵的直接上级的编号小于i。
输出格式
输出一个整数,表示派出敢死队的方案数。由于数目可能很大,你只需要输出这个数除10007的余数即可。
样例输入1
3
1 1
样例输出1
4
样例说明
这四种方式分别是:
1. 选1;
2. 选2;
3. 选3;
4. 选2, 3。
样例输入2
7
1 1 2 2 3 3
样例输出2
40
数据规模与约定
对于20%的数据,n ≤ 20;
对于40%的数据,n ≤ 100;
对于100%的数据,1 ≤ n ≤ 100000。
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 2000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。
提交时,注意选择所期望的编译器类型
解题思路 :
熟悉树形动态规划的都能看出这是个极度类似于'没有上司的宴会'的题目, 但是这题的规划方向还融合了组合数学的内容, 建议对这题完全没有思路的同学去看一下树形DP和没有上司的宴会, 在这里, 仅详述从子树到原树的状态转移关系.
1. 所有的结点都包含两个成员, beChoosedCount 和 beAbandonCount, 分别代表该结点被选择时带来的方案数 和 被抛弃时带来的方案数, 并且这两个值的初始化都是1, 因为无论如何, 被选是1种情况, 不被选也是1种情况. 比如说, 一个物品放在桌面上, 我们能对它有多少中操作方式呢 ? 大家都知道是2种 : 拿或不拿. 所以, 操作总数 = beChoosedCount
+ beAbandonCount.
2. 当已知下属子树的操作方案信息, 如何求原树的操作方案信息 ? 按着题意, 原树的根节点代表子树根节点的上司, 而且上司与下属只能选其一, 所以, 我们能有如下的关系式:
上司的beChoosedCount = 累乘( 下属的beAbandonCount );
上司的beAbandonCount = 累乘( 下属的总操作数 ) = 累乘( 下属的beChoosedCount + 下属的beAbandonCount );
为什么是累乘 ? 这属于一个选排列的数学问题, 这里不作详述.
有了上述关系式, 我们就可以写代码了, 存储结构依旧采用有向图的邻接表实现, 而不是一般树的左孩子右兄弟表示法.
算法分析 : 因为每个顶点只遍历一次, 所以整个算法的算法的时间复杂度为O(n), 从存储而言, 只存储了n个顶点和n-1条边, 所以空间复杂度为O(2n).
G将军有一支训练有素的军队,这个军队除开G将军外,每名士兵都有一个直接上级(可能是其他士兵,也可能是G将军)。现在G将军将接受一个特别的任务,需要派遣一部分士兵(至少一个)组成一个敢死队,为了增加敢死队队员的独立性,要求如果一名士兵在敢死队中,他的直接上级不能在敢死队中。
请问,G将军有多少种派出敢死队的方法。注意,G将军也可以作为一个士兵进入敢死队。
输入格式
输入的第一行包含一个整数n,表示包括G将军在内的军队的人数。军队的士兵从1至n编号,G将军编号为1。
接下来n-1个数,分别表示编号为2, 3, ..., n的士兵的直接上级编号,编号i的士兵的直接上级的编号小于i。
输出格式
输出一个整数,表示派出敢死队的方案数。由于数目可能很大,你只需要输出这个数除10007的余数即可。
样例输入1
3
1 1
样例输出1
4
样例说明
这四种方式分别是:
1. 选1;
2. 选2;
3. 选3;
4. 选2, 3。
样例输入2
7
1 1 2 2 3 3
样例输出2
40
数据规模与约定
对于20%的数据,n ≤ 20;
对于40%的数据,n ≤ 100;
对于100%的数据,1 ≤ n ≤ 100000。
资源约定:
峰值内存消耗 < 256M
CPU消耗 < 2000ms
请严格按要求输出,不要画蛇添足地打印类似:“请您输入...” 的多余内容。
所有代码放在同一个源文件中,调试通过后,拷贝提交该源码。
注意: main函数需要返回0
注意: 只使用ANSI C/ANSI C++ 标准,不要调用依赖于编译环境或操作系统的特殊函数。
注意: 所有依赖的函数必须明确地在源文件中 #include <xxx>, 不能通过工程设置而省略常用头文件。
提交时,注意选择所期望的编译器类型
解题思路 :
熟悉树形动态规划的都能看出这是个极度类似于'没有上司的宴会'的题目, 但是这题的规划方向还融合了组合数学的内容, 建议对这题完全没有思路的同学去看一下树形DP和没有上司的宴会, 在这里, 仅详述从子树到原树的状态转移关系.
1. 所有的结点都包含两个成员, beChoosedCount 和 beAbandonCount, 分别代表该结点被选择时带来的方案数 和 被抛弃时带来的方案数, 并且这两个值的初始化都是1, 因为无论如何, 被选是1种情况, 不被选也是1种情况. 比如说, 一个物品放在桌面上, 我们能对它有多少中操作方式呢 ? 大家都知道是2种 : 拿或不拿. 所以, 操作总数 = beChoosedCount
+ beAbandonCount.
2. 当已知下属子树的操作方案信息, 如何求原树的操作方案信息 ? 按着题意, 原树的根节点代表子树根节点的上司, 而且上司与下属只能选其一, 所以, 我们能有如下的关系式:
上司的beChoosedCount = 累乘( 下属的beAbandonCount );
上司的beAbandonCount = 累乘( 下属的总操作数 ) = 累乘( 下属的beChoosedCount + 下属的beAbandonCount );
为什么是累乘 ? 这属于一个选排列的数学问题, 这里不作详述.
有了上述关系式, 我们就可以写代码了, 存储结构依旧采用有向图的邻接表实现, 而不是一般树的左孩子右兄弟表示法.
#include <iostream> using namespace std; #define MOD 10007 class Vertex; class Edge{ // ...边 public: Edge *m_next; // ...出度顶点的下一条边 Vertex *m_to; // ...本边指向 Edge():m_next(NULL),m_to(NULL){} }; class Vertex{ // ...顶点 public: Edge *m_edge; // ...第一条出度边 int beChooseCount,beAbandonCount; // ...自身被选时的总方案数, 自身不被选时的总方案数 Vertex():m_edge(NULL){beChooseCount = beAbandonCount = 1;} void Add( Vertex* to, Edge *E){ // ...添加一条指向顶点to的边 E->m_next = m_edge; E->m_to = to; m_edge = E; } }; class Graph{ // ...图 public: Vertex *VSet; // ...顶点集 Edge *ESet; // ...边集 int ECount; // ...边的数量 Graph( int N ) { VSet = new Vertex[N+1]; ESet = new Edge ; ECount = 0; } inline void Add( int v1, int v2 ){ // ...添加一条从v1到v2的边 VSet[v1].Add( &VSet[v2] , ESet + ECount++ ); } ~Graph(){ delete VSet; delete ESet; } int GetCount(){ // ...获取总方案数 DFS( &VSet[1] ); return (VSet[1].beAbandonCount + VSet[1].beChooseCount - 1)%MOD; // ...减去一种全空的可能 } static void DFS( Vertex *V){ // ...搜索顶点V的方案信息 for( Edge *e = V->m_edge; e!=NULL; e=e->m_next ){ DFS( e->m_to ); // ...先求子树信息 // ...若原树结点能被选, 则累乘所有子树结点不能被选的方案数 V->beChooseCount = V->beChooseCount * e->m_to->beAbandonCount %MOD; // ...若原树结点不能被选, 则累乘所有子树结点的总方案数 V->beAbandonCount = V->beAbandonCount * (e->m_to->beChooseCount+e->m_to->beAbandonCount )%MOD; } } }; int main(int argc, char** argv) { int N,superior; // ...士兵数, 上司序号 cin >> N; Graph G(N); for( int i = 2; i <= N; G.Add( superior,i++) ) cin >> superior; cout << G.GetCount(); return 0; }
算法分析 : 因为每个顶点只遍历一次, 所以整个算法的算法的时间复杂度为O(n), 从存储而言, 只存储了n个顶点和n-1条边, 所以空间复杂度为O(2n).
相关文章推荐
- 第六届蓝桥杯试题--生命之树 解题报告
- 第六届蓝桥杯省赛试题--垒骰子 解题报告
- 第六届蓝桥杯省赛试题--垒骰子 以矩阵的方法实现 解题报告
- 2014 第六届蓝桥杯校内赛解题报告 答案
- 蓝桥杯校内选拔赛 派遣敢死队
- 蓝桥杯 历届试题 买不到的数目 解题报告(完全背包,数论)
- 蓝桥杯 历届试题 危险系数 解题报告(并查集)
- 蓝桥杯 历届试题 九宫重排 解题报告(BFS,双向BFS优化)
- 第六届蓝桥杯大赛个人赛省赛(软件类)C++A组 解题报告
- 第六届蓝桥杯2015本科B组c/++部分解题报告
- 2015年第六届蓝桥杯省赛(C/C++ B组)解题报告
- 第六届 蓝桥杯 第七题 手链样式 枚举 暴力 解题报告
- 第六届蓝桥杯校内选拔赛C/C++高职组解题(1)
- 第九届蓝桥杯选拔赛C语言组 解题报告索引
- 第六届蓝桥杯校内选拔赛C/C++高职组解题(2)
- 第六届蓝桥杯校内选拔赛C/C++高职组解题(6)
- 2015年第六届蓝桥杯省赛(C/C++ B组)解题报告
- 第六届蓝桥杯校内选拔赛C/C++高职组解题(3)
- 第六届蓝桥杯校内选拔赛C/C++高职组解题(7)
- NYNU ACM 蓝桥杯选拔赛 解题报告