<更新提示>

<第一次更新>


<正文>

快乐链覆盖

Description

给定一棵 n 个点的树,你需要找至多 k 条互不相交的路径,使得它们的长度之和最大

定义两条路径是相交的:当且仅当存在至少一个点,使得这个点在两条路径中都出现

定义一条路径的长度为该路径经过的点的数量

这个题非常简单,非常传统,但为了让它变成一道能一个顶俩的题,出题人决定让你输出任意一组方案。

Input Format

第一行一个正整数 T 表示数据组数

接下来,对于每组数据:

第一行两个整数 n,kn,k

接下来 n−1 行,每行两个整数 a,b 描述一条树边

Output Format

对于每组数据,第一行输出一个整数表示最大的长度之和

之后你要输出任意一组长度之和最大的方案之后你要输出任意一组长度之和最大的方案,第一行一个整数 P 表示你的方案由几条路径构成,接下来 P 行每行两个整数描述一条路径

当然,如果你只能求出答案,不能求出任意一组方案的话,本题也会给你一些部分分,具体看数据范围中的描述

但是请注意:你必须保证你的输出的格式是正确的,首先你输出的 P 必须不超过 k 且是非负整数,且无论你的方案正不正确,后面都要描述 P 条路径,例如你输出 P=2 但是只输出了一条路径的话,后面的其他组的输出可能就会被读入作为第二条路径,这会导致你得不到该有的分数

Sample Input

1
5 2
1 2
1 3
1 4
1 5

Sample Output

4
2
5 5
4 3

解析

这显然是一道树形\(dp\),我们可以先不考虑输出方案。

我们可以先设置简单的状态\(f[x][k]\)代表以\(x\)为根的子树中选了\(k\)条链的最大长度之和,但是我们发现好像不太适合从子树转移。

我们可以考虑一下从子树上转移过来会有几种情况,一个就是根节点\(x\)不包含在任何一条链中,还有就是根节点\(x\)包含在某个链中。

如果\(x\)包含在某个链中,还有两种情况:\(1.\) \(x\)现在是某个链的一端,也就是说,这条链还可以继续向上拓展。 \(2.\) \(x\)是某条链中间的一个点,也就是说有两条链在点\(x\)处合并了。

那样就可以设置状态了,\(f[x][k][0/1/2]\)就分别代表了如上的三种情况。

于是我们可以考虑合并,最简单的方式就是先不考虑这棵子树影响了根节点\(x\),把所有状态先暴力合并了,然后再考虑这棵子树向上延展,或者与其他链合并这两种影响根节点\(x\)的情况,单独转移。

这种复杂的树形\(dp\)由于没有显式地划分阶段,所以建议用滚动数组的方式转移,不建议直接转移,那样容易出现\(dp\)顺序的问题。

然后我们再考虑输出方案。一般的\(dp\)输出方案的方式都是记录每一个状态是从哪些状态转移而来的,但是显然这种背包类的\(dp\)不适合,他可能是由很多子节点共同转移过来的,直接记录空间是不够的。

但是我们考虑到一个节点顶多也就是给了他父亲一个贡献,所以我们可以换一种记录方式,把转移记录在子节点上。

记录了方案之后,我们就可以再通过一遍\(dfs\)输出了。当然,我们仍然需要对转移进行讨论,以便找到链的两个端点。

\(Code:\)

#include <bits/stdc++.h>
using namespace std;
const int N = 10020 , K = 520 , INF = 0x3f3f3f3f;
inline int read(void)
{
int x = 0 , w = 0; char ch = ' ';
while ( !isdigit(ch) ) w |= ch == '-' , ch = getchar();
while ( isdigit(ch) ) x = x * 10 + ch - 48 , ch = getchar();
return w ? -x : x;
}
int n,k,f[N][K][3],dp[K][3],size[N];
vector < pair <int,int> > ans;
pair < int , int > fr[N][K][3];
vector < int > e[N];
inline void input(void)
{
n = read() , k = read();
for (int i=1;i<n;i++)
{
int x = read() , y = read();
e[x].push_back( y );
e[y].push_back( x );
}
}
inline bool upd(int &a,int b) { if ( b > a ) return a = b , true; return false; }
inline void merge(int x,int y)
{
memcpy( dp , f[x] , sizeof f[x] );
for (int i=0;i<=size[x];i++)
for (int j=0;j<=size[y]&&i+j<=k+1;j++)
{
if ( i + j <= k )
{
for (int a=0;a<3;a++)
for (int b=0;b<3;b++)
if ( upd( dp[i+j][a] , f[x][i][a] + f[y][j][b] ) )
fr[y][i+j][a] = make_pair( j , b );
if ( j > 0 )
if ( upd( dp[i+j][1] , f[x][i][0] + f[y][j][1] + 1 ) )
fr[y][i+j][1] = make_pair( j , -1 );
}
if ( i + j > 1 && j > 0 )
if ( upd( dp[i+j-1][2] , f[x][i][1] + f[y][j][1] ) )
fr[y][i+j-1][2] = make_pair( j , -1 );
}
size[x] += size[y];
memcpy( f[x] , dp , sizeof dp );
}
inline void dfs(int x,int fa)
{
for (int i=0;i<=k;i++)
for (int j=0;j<3;j++)
f[x][i][j] = -INF , fr[x][i][j] = make_pair(-INF,-INF);
size[x] = 1 , f[x][0][0] = 0;
f[x][1][1] = f[x][1][2] = 1;
for ( auto y : e[x] )
if ( y != fa )
dfs( y , x ) , merge( x , y );
}
inline int findpath(int x,int fa,int cnt,int op)
{
if ( !cnt ) return 0;
reverse( e[x].begin() , e[x].end() );
int _op,cur,c,t;
_op = op , cur = _op == 1 ? x : 0;
if ( op < 0 ) op = 1;
for ( int y : e[x] )
if ( y != fa )
{
if ( cnt == 0 ) break;
tie(c,t) = fr[y][cnt][op];
if ( t == -INF ) continue;
if ( t == -1 )
{
int ver = findpath( y , x , c , t );
if ( ver != 0 )
{
if ( cur )
ans.push_back( make_pair(cur,ver) ) , cur = 0;
else cur = ver;
}
cnt -= c - (--op);
}
else cnt -= c , findpath(y, x, c, t);
}
if ( _op > 0 && cur )
ans.push_back( make_pair(cur,cur) ) , cur = 0;
else if ( _op < 1 && !cur ) return x;
return cur;
}
inline void reset(void)
{
ans.clear();
for (int i=1;i<=n;i++)
e[i].clear();
}
int main(void)
{
int T; scanf("%d",&T);
ans.clear();
while ( T --> 0 )
{
input();
dfs( 1 , 0 );
int num = 0 , op = 0;
for (int i=1;i<=k;i++)
for (int j=0;j<3;j++)
if ( f[1][i][j] > f[1][num][op] )
num = i , op = j;
printf("%d\n",f[1][num][op]);
findpath( 1 , 0 , num , op );
printf("%d\n",ans.size());
for ( auto p : ans )
{
int x,y;
tie( x , y ) = p;
printf("%d %d\n",x,y);
}
reset();
}
return 0;
}

<后记>

『快乐链覆盖 树形dp』的更多相关文章

  1. 『战略游戏 最大利润 树形DP』

    通过两道简单的例题,我们来重新认识树形DP. 战略游戏(luoguP1026) Description Bob喜欢玩电脑游戏,特别是战略游戏.但是他经常无法找到快速玩过游戏的办法.现在他有个问题.他要 ...

  2. 『没有上司的舞会 树形DP』

    树形DP入门 有些时候,我们需要在树形结构上进行动态规划来求解最优解. 例如,给定一颗\(N\)个节点的树(通常是无根树,即有\(N-1\)条无向边),我们可以选择任意节点作为根节点从而定义出每一颗子 ...

  3. 『大 树形dp』

    大 Description 滑稽树上滑稽果,滑稽树下你和我,滑稽树前做游戏,滑稽多又多.树上有 n 个节点,它们构成了一棵树,每个节点都有一个滑稽值. 一个大的连通块是指其中最大滑稽值和最小滑稽值之差 ...

  4. 『You Are Given a Tree 整体分治 树形dp』

    You Are Given a Tree Description A tree is an undirected graph with exactly one simple path between ...

  5. 『kamp 树形dp』

    kamp Description jz 市的云台山是个很美丽的景区,小 x 暑期到云台山打工,他的任务是开景区的大巴. 云台山景区有 N 个景点,这 N 个景点由 N-1 条道路连接而成,我们保证这 ...

  6. 『树上匹配 树形dp』

    树上匹配 Description 懒惰的温温今天上班也在偷懒.盯着窗外发呆的温温发现,透过窗户正巧能看到一棵 n 个节点的树.一棵 n 个节点的树包含 n-1 条边,且 n 个节点是联通的.树上两点之 ...

  7. 【bzoj1907】树的路径覆盖 树形dp

    题目描述 输入 输出 样例输入 1 7 1 2 2 3 2 4 4 6 5 6 6 7 样例输出 3 题解 树形dp 设f[x]表示以x为根的子树完成路径覆盖,且x为某条路径的一端(可以向上延伸)的最 ...

  8. (中等) HDU 5293 Tree chain problem,树链剖分+树形DP。

    Problem Description   Coco has a tree, whose vertices are conveniently labeled by 1,2,…,n.There are ...

  9. 『保卫王国 树上倍增dp』

    保卫王国 Description Z 国有n座城市,n - 1条双向道路,每条双向道路连接两座城市,且任意两座城市 都能通过若干条道路相互到达. Z 国的国防部长小 Z 要在城市中驻扎军队.驻扎军队需 ...

随机推荐

  1. 21个React开发神器

    摘要: React开发神器. 原文:22 Miraculous Tools for React Developers in 2019 译者:前端小智 下列工具中的重要性与排序无关. 1.Webpack ...

  2. 201871010136-赵艳强《面向对象程序设计(Java)》第八周学习总结

    201871010136-赵艳强<面向对象程序设计(Java)>第八周学习总结 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu-daizh/ 这 ...

  3. 201871020225-牟星源《面向对象程序设计(java)》第十周学习总结

    201871020225-牟星源<面向对象程序设计(java)>第十周学习总结 博文正文开头: 项目 内容 这个作业属于哪个课程 https://www.cnblogs.com/nwnu- ...

  4. python字典基本操作

    字典是python中五中基本数据类型之一,虽然它的赋值稍微麻烦点,但用起来真的是很方便.它用键值对来存放数据,所谓键值对,就是一个键,对应一个值,如果后面对前面的键再次赋值,第一次的值就被覆盖掉.像是 ...

  5. 没有重写接口方法,IDEA没有报错。

    今天在IDEA写拦截器的时候遇到点困惑,继承了HandlerInterceptor没有报错,我一直认为他会提醒,要重写方法.如下图 通过查资料,嗯,终于找到原因来,先来上HandlerIntercep ...

  6. xgboost:

    https://www.zybuluo.com/Dounm/note/1031900 GBDT算法详解 http://mlnote.com/2016/10/05/a-guide-to-xgboost- ...

  7. 批处理简单命令 start

    start 命令 调用外部程序,所有的DOS命令和命令行程序都可以由start命令来调用. 如:start calc.exe 即可打开Windows的计算器. 常用参数: MIN 开始时窗口最小化 S ...

  8. scrapy机制mark(基于twisted)

    twisted twisted管理了所有的异步任务 Twisted的主线程是单线程的,即reactor线程: 而这些io耗时操作会在线程池中运行,不再twisted主线程中运行,即通过线程池来执行异步 ...

  9. 1-开发共享版APP(搭建指南)-快速搭建到自己的服务器

    该APP安装包下载链接: http://www.mnif.cn/appapk/IotDevelopmentVersion/20190820/app-debug.apk 或者扫描二维码下载 注:该下载可 ...

  10. 解决PEnetwork启动的时候提示"An error occured while starting the "TCP/IP Registry Compatibility" Service (2)!"程序将立即退出的问题

    解决PEnetwork启动的时候提示"An error occured while starting the "TCP/IP Registry Compatibility" ...