您的位置:首页 > 大数据 > 人工智能

hdu5758 2016 Multi-University Training Contest 3 Explorer Bo 解题报告

2016-09-15 21:53 417 查看
感觉最近都没状态不知道为什么。。。。一道不难的题不会做,看了题解发现自己好蠢。。。。

题意:给一颗n个节点的树,问你用最少的链覆盖这棵树上所有的边(可以重复覆盖),链的长度和最小为多少

链的长度=覆盖的边的数量

题解:

树形DP

首先,如果要覆盖,最少需要(n+1)/2条链,链的两端肯定是2个叶子节点,如果叶子节点数是奇数,那么有一条链由一个叶子向上覆盖他父亲连向他的边就停止

那么,定义f[x]为以x为根的子树内用最少的链覆盖,链的最小长度和,

首先一棵子树里的叶子肯定先两两匹配,再考虑和子树根的父亲的连边

如果他的一个孩子y的子树内叶子节点数为奇数,就一定有1条链连上来(为了覆盖x和y的边),对f[x]贡献f[y]+1

如果他的一个孩子y的子树内叶子节点数为偶数,就一定有2条链连上来(首先为了覆盖x和y的边,至少要有一条链,那这个叶子连上来,剩余的原来和他匹配的点没有点匹配,故也连一条链上来),对f[x]贡献f[y]+2

最后,求完整棵树的f后,对整棵树考虑,

如果整棵树的叶子节点数是偶数,可以直接输出根的f值

如果是奇数,那么会有一条链叶子连上来的链没有叶子匹配,事实上这条链也不用连上来,只要连向这个叶子的父亲即可,所以对于这种情况,我们要撤销一条链,考虑撤销一条链的影响,

从这个叶子走上去,如果x到y这条边之前只被这一条链覆盖,撤消后无边覆盖,就要像上面的情况一样,走两条边上去,答案+1,

如果这条边之前被覆盖了2次,那么撤销了没有影响,答案-1,遍历整棵树找到使答案最小的就行了

注意一个地方,就是如果你选的根是一个叶子节点,那整棵树的叶子节点数要+1,根是不能例外的

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<ctime>
#include<cmath>
#include<string>
#include<bitset>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define LL long long
using namespace std;

const int maxn = 110000;
struct edge
{
int y,next;
}a[maxn<<1]; int len,first[maxn];
LL f[maxn];
int siz[maxn];
int n,m;

void ins( int x,int y )
{
len++;
a[len].y = y;
a[len].next = first[x]; first[x] = len;
}
void dfs( int x,int fa )
{
bool flag = true;
siz[x] = 0; f[x] = 0;
for( int k=first[x];k;k=a[k].next )
{
int y = a[k].y;
if( y != fa )
{
dfs( y,x );
flag = false;
siz[x] += siz[y];
if( siz[y]&1 ) f[x] += f[y]+1;
else f[x] += f[y]+2;
}
}
if( flag ) siz[x] = 1;
}
LL fm( int x,int fa )
{
LL mx = 0;
for( int k=first[x];k;k=a[k].next )
{
int y = a[k].y;
if( y != fa )
{
LL temp = fm( y,x );
temp += (siz[y]&1)?-1:1;
if( temp > mx ) mx = temp;
}
}
return mx;
}
int main()
{
int t;
scanf("%d",&t);
while( t-- )
{
memset( first,0,sizeof first ); len = 0;
int x,y;
scanf("%d",&n);
for( int i=1;i<n;i++ )
{
scanf("%d%d",&x,&y);
ins( x,y ); ins( y,x );
}
dfs( 1,0 );
if( a[first[1]].next == 0 ) siz[1]++;
if( siz[1]&1 ) f[1] -= fm( 1,0 );
printf("%lld\n",f[1]);

}

return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: