HDU 5758 Explorer Bo (树形DP)
2016-07-27 14:46
393 查看
题目:给你一棵树,用最少的链去覆盖这棵树,求链的最小总长度。
解析:num为叶子节点数,显然链数是(num+1)/2。如果是偶数,就是叶子节点到叶子节点,如果是奇数,那么就是在奇数-1情况下的树下加一条叶子到其祖先的链。
偶数的情况:从一个非叶子节点出发,如果其子节点的叶子节点是偶数,则ans+=2,如果是奇数,ans+=1。
奇数的情况:枚举一下那条单链所在的子树。
设dp[u][i][j] 在u的子树中,u的父边需要经过i次,j表示单链是否在该子树中。j=1就额外枚举单链所在子树即可,如果单链在该子树中,那么该树中的有效叶子节点-1(即奇数变偶数,偶数变奇数)。
[code]:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn = 2e5+5;
struct Nod{
int b,next;
void init(int b,int next){
this->b=b;this->next=next;
}
}buf[2*maxn];
int n,E[maxn],len,cnt[maxn],ans,deg[maxn];
int dp[maxn][3][2];
void init(){
ans = len = 0;
memset(E,-1,sizeof(E));
memset(dp,-1,sizeof(dp));
memset(deg,0,sizeof(deg));
}
void add_edge(int a,int b){
buf[len].init(b,E[a]);E[a]=len++;
buf[len].init(a,E[b]);E[b]=len++;
deg[a]++;deg[b]++;
}
void pre_dfs(int u,int pre){
int i,v,f = 0;
cnt[u] = 0;
for(i = E[u];i != -1;i = buf[i].next){
v = buf[i].b;
if(v == pre) continue;
pre_dfs(v,u);
cnt[u]+=cnt[v];
f = 1;
}
if(!f) cnt[u] = 1;
}
int dfs(int u,int pre,int s1,int s2){
if(dp[u][s1][s2]!=-1) return dp[u][s1][s2];
int i,v,ans = 0,num = -s1,tmp;
for(i = E[u];i != -1;i = buf[i].next){
v = buf[i].b;
if(v == pre) continue;
num += cnt[v];
if(cnt[v]&1) ans += dfs(v,u,1,0)+1;
else ans += dfs(v,u,2,0)+2;
}
if(s2){
tmp = 0x3f3f3f3f;
for(i = E[u];i != -1;i = buf[i].next){
v = buf[i].b;
if(v == pre) continue;
if(cnt[v]&1){
if(cnt[v]==1) tmp = min(tmp,ans);
else tmp = min(tmp,ans+dfs(v,u,2,1)+2-(dfs(v,u,1,0)+1));
}else tmp = min(tmp,ans+dfs(v,u,1,1)+1-(dfs(v,u,2,0)+2));
}
ans = tmp;
}
return dp[u][s1][s2] = ans;
}
int main(){
int i,j,cas,u,v;
scanf("%d",&cas);
while(cas--){
scanf("%d",&n);
init();
for(i = 1;i < n;i++){
scanf("%d%d",&u,&v);
add_edge(u,v);
}
if(n == 2){
puts("1");
continue;
}
int root=-1,num = 0;
for(i = 1;i <= n;i++){
if(deg[i]!=1&&root==-1){
root = i;
}
num += deg[i]==1;
}
pre_dfs(root,-1);
printf("%d\n",dfs(root,-1,0,num&1));
}
return 0;
}
解析:num为叶子节点数,显然链数是(num+1)/2。如果是偶数,就是叶子节点到叶子节点,如果是奇数,那么就是在奇数-1情况下的树下加一条叶子到其祖先的链。
偶数的情况:从一个非叶子节点出发,如果其子节点的叶子节点是偶数,则ans+=2,如果是奇数,ans+=1。
奇数的情况:枚举一下那条单链所在的子树。
设dp[u][i][j] 在u的子树中,u的父边需要经过i次,j表示单链是否在该子树中。j=1就额外枚举单链所在子树即可,如果单链在该子树中,那么该树中的有效叶子节点-1(即奇数变偶数,偶数变奇数)。
[code]:
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<algorithm>
using namespace std;
const int maxn = 2e5+5;
struct Nod{
int b,next;
void init(int b,int next){
this->b=b;this->next=next;
}
}buf[2*maxn];
int n,E[maxn],len,cnt[maxn],ans,deg[maxn];
int dp[maxn][3][2];
void init(){
ans = len = 0;
memset(E,-1,sizeof(E));
memset(dp,-1,sizeof(dp));
memset(deg,0,sizeof(deg));
}
void add_edge(int a,int b){
buf[len].init(b,E[a]);E[a]=len++;
buf[len].init(a,E[b]);E[b]=len++;
deg[a]++;deg[b]++;
}
void pre_dfs(int u,int pre){
int i,v,f = 0;
cnt[u] = 0;
for(i = E[u];i != -1;i = buf[i].next){
v = buf[i].b;
if(v == pre) continue;
pre_dfs(v,u);
cnt[u]+=cnt[v];
f = 1;
}
if(!f) cnt[u] = 1;
}
int dfs(int u,int pre,int s1,int s2){
if(dp[u][s1][s2]!=-1) return dp[u][s1][s2];
int i,v,ans = 0,num = -s1,tmp;
for(i = E[u];i != -1;i = buf[i].next){
v = buf[i].b;
if(v == pre) continue;
num += cnt[v];
if(cnt[v]&1) ans += dfs(v,u,1,0)+1;
else ans += dfs(v,u,2,0)+2;
}
if(s2){
tmp = 0x3f3f3f3f;
for(i = E[u];i != -1;i = buf[i].next){
v = buf[i].b;
if(v == pre) continue;
if(cnt[v]&1){
if(cnt[v]==1) tmp = min(tmp,ans);
else tmp = min(tmp,ans+dfs(v,u,2,1)+2-(dfs(v,u,1,0)+1));
}else tmp = min(tmp,ans+dfs(v,u,1,1)+1-(dfs(v,u,2,0)+2));
}
ans = tmp;
}
return dp[u][s1][s2] = ans;
}
int main(){
int i,j,cas,u,v;
scanf("%d",&cas);
while(cas--){
scanf("%d",&n);
init();
for(i = 1;i < n;i++){
scanf("%d%d",&u,&v);
add_edge(u,v);
}
if(n == 2){
puts("1");
continue;
}
int root=-1,num = 0;
for(i = 1;i <= n;i++){
if(deg[i]!=1&&root==-1){
root = i;
}
num += deg[i]==1;
}
pre_dfs(root,-1);
printf("%d\n",dfs(root,-1,0,num&1));
}
return 0;
}
相关文章推荐
- poj1201 intervals
- Linux反向过滤导致网卡无法从外网ping通
- HDU5733 tetrahedron[计算几何]
- 三种主流的WebService实现方案(REST/SOAP/XML-RPC)简述及比较
- Netperf - The fastest TCP connection with Multipath TCP
- 字节流和字符流的区别
- jsp el比较字符串
- 关于无法加载DLL"***.dll":找不到指定的模块(异常来自HRESULT:0x8007007E)问题的解决办法
- 源码-PL/SQL从入门到精通-第十八章-PL/SQL性能优化建议-Part 1
- OpenCV学习笔记——滑动条开关
- Intellij Idea2016.2开发工具注册
- linux查看版本号、更改主机名、位数
- Inno Setup 注册表启动项 修改注册表
- 门户更新开发计划
- $.ajax()
- 计词unigram和bigram的频次
- AndroidAnnotations使用配置整合版
- Google Protocol Buffer 的使用和原理
- MUI 拨打电话
- android-Service和Thread的区别