[hdu4714 Tree2cycle]树形DP

题意:给一棵树,删边和加边的代价都为1,求把树变成一个圈所花的最小代价。

思路:对原树进行删边操作,直到将原树分成若干条链,然后通过在链之间添加边形成圈,由于删边和加边一一对应,且最后需要额外一条边连成圈,所以有:

最小代价=(最小链数-1)*2+1=最小链数*2-1。

令dp[i][0]表示i不和i的父亲相连,i这颗子树所形成的最少链数,dp[i][1]表示i和i的父亲相连,i这颗子树所形成的最少链数,则:

dp[i][0]=∑dp[j][0]+dp[p][1]-dp[p][0] + dp[q][1]-dp[q][0]-1

dp[i][1]=∑dp[j][0]+dp[p][1]-dp[p][0]

其中,j是i的子节点,p是满足dp[j][1]-dp[j][0]最小的j,q是满足dp[j][1]-dp[j][0]第二小的j。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
#pragma comment(linker, "/STACK:10240000")
#include <map>
#include <set>
#include <cmath>
#include <ctime>
#include <deque>
#include <queue>
#include <stack>
#include <vector>
#include <cstdio>
#include <string>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <algorithm>

using namespace std;

#define X                   first
#define Y                   second
#define pb                  push_back
#define mp                  make_pair
#define all(a)              (a).begin(), (a).end()
#define fillchar(a, x)      memset(a, x, sizeof(a))
#define copy(a, b)          memcpy(a, b, sizeof(a))

typedef long long ll;
typedef pair<int, int> pii;
typedef unsigned long long ull;

//#ifndef ONLINE_JUDGE
void RI(vector<int>&a,int n){a.resize(n);for(int i=0;i<n;i++)scanf("%d",&a[i]);}
void RI(){}void RI(int&X){scanf("%d",&X);}template<typename...R>
void RI(int&f,R&...r){RI(f);RI(r...);}void RI(int*p,int*q){int d=p<q?1:-1;
while(p!=q){scanf("%d",p);p+=d;}}void print(){cout<<endl;}template<typename T>
void print(const T t){cout<<t<<endl;}template<typename F,typename...R>
void print(const F f,const R...r){cout<<f<<", ";print(r...);}template<typename T>
void print(T*p, T*q){int d=p<q?1:-1;while(p!=q){cout<<*p<<", ";p+=d;}cout<<endl;}
//#endif
template<typename T>bool umax(T&a, const T&b){return b<=a?false:(a=b,true);}
template<typename T>bool umin(T&a, const T&b){return b>=a?false:(a=b,true);}

const double PI = acos(-1.0);
const int INF = 1e9 + 7;
const double EPS = 1e-12;

/* -------------------------------------------------------------------------------- */

const int maxn = 1e6 + 7;

pii E[maxn * 2];
int SZ;
int Next[maxn * 2];
int last[maxn];
int son[maxn];

void add(int u, int v) {
    E[SZ ++] = mp(u, v);
    E[SZ ++] = mp(v, u);
    Next[SZ - 2] = last[u];
    last[u] = SZ - 2;
    Next[SZ - 1] = last[v];
    last[v] = SZ - 1;
}

int vis[maxn], dp[maxn][2];

void dfs(int rt) {
    vis[rt] = true;
    int ans = 0, minv1 = INF, minv2 = INF, sum = 0, sum1 = 0, cnt = 0;
    for (int i = last[rt]; ~i; i = Next[i]) {
        int v = E[i].Y;
        if (!vis[v]) {
            dfs(v);
            cnt ++;
            sum += dp[v][0];
            sum1 += dp[v][1];
            int buf = dp[v][1] - dp[v][0];
            if (buf < minv1) {
                minv2 = minv1;
                minv1 = buf;
            }
            else {
                if (buf < minv2) minv2 = buf;
            }
        }
    }
    if (cnt == 0) dp[rt][0] = dp[rt][1] = 1;
    else if (cnt == 1) dp[rt][0] = dp[rt][1] = sum1;
    else if (cnt >= 2) {
        dp[rt][0] = sum + minv1 + minv2 - 1;
        dp[rt][1] = sum + minv1;
    }
//    printf("%d: %d %d\n", rt, dp[rt][0], dp[rt][1]);
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("in.txt", "r", stdin);
    //freopen("out.txt", "w", stdout);
#endif // ONLINE_JUDGE
    int T;
    cin >> T;
    while (T --) {
        int n;
        cin >> n;
        SZ = 0;
        fillchar(last, - 1);
        fillchar(Next, - 1);
        for (int i = 1; i < n; i ++) {
            int u, v;
            scanf("%d%d", &u, &v);
            add(u, v);
        }
        fillchar(vis, 0);
        dfs(1);
        cout << dp[1][0] * 2 - 1 << endl;
    }
    return 0;
}
时间: 2024-11-05 08:09:20

[hdu4714 Tree2cycle]树形DP的相关文章

HDU 4714:Tree2cycle 树形DP

Tree2cycle 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=4714 题意: 给出一棵树,可以删边和加边将这棵树变成一个圈(没有任何分支),求最少的操作次数. 题解: 将树改造为圈可以通过现将树变成一条没有分叉的链后再加上一条边连接链的两端. 再简化一下就变成了选一条路径作为主干,再将其他所有分叉通过增删边连接到竹竿上的过程. 设DP1[i](把以 i 为根节点的子树作为主干的一部分),和DP2[i](把以 i 为根节点的子树作为分叉),

HDU 4714 Tree2cycle (树形DP)

题意:给定一棵树,断开一条边或者接上一条边都要花费 1,问你花费最少把这棵树就成一个环. 析:树形DP,想一想,要想把一棵树变成一个环,那么就要把一些枝枝叶叶都换掉,对于一个分叉是大于等于2的我们一定要把它从父结点上剪下来是最优的, 因为如果这样剪下来再粘上花费是2(先不管另一端),如果分别剪下来再拼起来,肯定是多花了,因为多了拼起来这一步,知道这,就好做了.先到叶子结点, 然后再回来计算到底要花多少. 代码如下: #pragma comment(linker, "/STACK:10240000

hdu 4717 Tree2cycle(树形DP)

Tree2cycle Time Limit: 15000/8000 MS (Java/Others)    Memory Limit: 102400/102400 K (Java/Others)Total Submission(s): 2161    Accepted Submission(s): 508 Problem Description A tree with N nodes and N-1 edges is given. To connect or disconnect one edg

hdu4714(树形dp)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4714 题意:给你一棵树,,其中每去掉一条边或加一条边的代价均为1,让你求出将其变成一个圆的最小代价. 分析:由于该树要形成一个圆,所以对于分支大于等于2的子树,必须把它断开形成一条链,最后再连接起来. 定义N为1000010时不断超时,定义N为2000010时却1357ms,hdu这破oj真是让人无语... #pragma comment(linker,"/STACK:102400000,10240

HDU-2196 Computer (树形DP)

最近在看树形DP,这题应该是树形DP的经典题了,写完以后还是有点感觉的.之后看了discuss可以用树分治来做,以后再试一试. 题目大意 找到带权树上离每个点的最远点.︿( ̄︶ ̄)︿ 题解: 对于每一个点的最远点,就是以这个点为根到所有叶子节点的最长距离.但是如果确定根的话,除了根节点外,只能找到每个节点(度数-1)个子树的最大值,剩下一个子树是该节点当前的父亲节点. 所以当前节点的最远点在当前节点子树的所有叶子节点以及父亲节点的最远点上(当父亲节点的最远点不在当前节点的子树上时), 如果父亲节

UVA-01220 Party at Hali-Bula (树形DP+map)

题目链接:https://vjudge.net/problem/UVA-1220 思路: 树形DP模板题,求最大人数很简单,难点在于如何判断最大人数的名单是否有不同的情况: 解决方法是用一个数组f[manx][2]记录该节点是否出场的情况,为真时代表有多种情况; 具体讨论: 当父节点的值加上某个子节点的值时,他的f的情况也和该子节点一样: 当某个节点dp(i, 0) == dp(i, 1), 则该节点以及它的父节点也一定有多种情况(父节点必定取其中之一). Code: 1 #include<bi

HDU 1520 树形dp裸题

1.HDU 1520  Anniversary party 2.总结:第一道树形dp,有点纠结 题意:公司聚会,员工与直接上司不能同时来,求最大权值和 #include<iostream> #include<cstring> #include<cmath> #include<queue> #include<algorithm> #include<cstdio> #define max(a,b) a>b?a:b using nam

HDU2196 Computer(树形DP)

和LightOJ1257一样,之前我用了树分治写了.其实原来这题是道经典的树形DP,感觉这个DP不简单.. dp[0][u]表示以u为根的子树中的结点与u的最远距离 dp[1][u]表示以u为根的子树中的结点与u的次远距离 这两个可以一遍dfs通过儿子结点转移得到.显然dp[0][u]就是u的一个可能的答案,即u往下走的最远距离,还缺一部分就是u往上走的最远距离: dp[2][u]表示u往上走的最远距离 对于这个的转移,分两种情况,是这样的: dp[2][v] = max( dp[0][u]+w

hdu5593--ZYB&#39;s Tree(树形dp)

问题描述 ZYB有一颗N个节点的树,现在他希望你对于每一个点,求出离每个点距离不超过KK的点的个数. 两个点(x,y)在树上的距离定义为两个点树上最短路径经过的边数, 为了节约读入和输出的时间,我们采用如下方式进行读入输出: 读入:读入两个数A,B,令fai??为节点i的父亲,fa?1??=0;fa?i??=(A∗i+B)%(i−1)+1,i∈[2,N] . 输出:输出时只需输出N个点的答案的xor和即可. 输入描述 第一行一个整数TT表示数据组数. 接下来每组数据: 一行四个正整数N,K,A,