0
点赞
收藏
分享

微信扫一扫

Weight the Tree(树形dp)

霍华德 2022-03-16 阅读 36

题目Link
题目学习link1
题目学习link2
题目学习link3
%%% 受益匪浅!
--------------------------------

通过了解题意可知
当 n = 2 时 为一种特殊情况,特判一下(样例温和捏~
当 n > 2 时 当一个节点为good节点时,与其相连的节点一定为非good点
这样模型就显现出来了 类似于link2中所讲树的最小点覆盖问题
这里用 0 表示 该点不是good点 , 1 表示为good点
且只有 0-0 , 0- 1 两种情况,dp即可求出最大的 good 点个数
由于还要求最小的节点权值和,开个结构体用dp数组一起求
状态转移见代码

/*
    *@author:bzdhxs
    *@date:2022/03/15
    *@URL:
*/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<string>
#include<vector>
#include<map>
#include<queue>
using namespace std;
template <typename T>
inline void read(T &s){s = 0;T w = 1, ch = getchar();while (!isdigit(ch)) { if (ch == '-') w = -1; ch = getchar(); }while (isdigit(ch))  { s = (s << 1) + (s << 3) + (ch ^48); ch = getchar();} s *= w;}
template <typename T>
inline void write(T s){if (s < 0) putchar('-'), s = -s;if (s > 9) write(s / 10);putchar(s % 10 + '0');}
#define int long long
#define _orz ios::sync_with_stdio(false),cin.tie(0)
#define mem(str,num) memset(str,num,sizeof(str))
#define forr(i,a,b) for(int i = a; i <= b;i++)
#define forn(i,n) for(int i = 0; i < n; i++)
#define dbg() cout <<"0k!"<< endl;
typedef long long ll;
int pri[16] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53};
const int inf = 0x3f3f3f3f;
const int INF = ~0ULL;
const int N = 1e6+10;

int n;
vector<int> g[200005];
struct node{
    int g,s; // g 记录好点个数  s 记录权值和
    //用来取较优的点
    bool operator>(const node&t)const{
        if(g != t.g) return g > t.g; //优先good点个数,相同取权值小的
        return s < t.s;
    }
}dp[200005][2];

//树形dp
void dfs1(int u,int fa){
    dp[u][0].s = 1;// 非good点权值为1,最小
    dp[u][1].s = g[u].size();// 作为good点权值为直接连边的个数
    dp[u][1].g = 1;// 为good点故为1
 
    for(auto v:g[u]){
        if(v == fa) continue;
        dfs1(v,u);
        // u作为good点
        dp[u][1].g += dp[v][0].g; // 直接连边的均为非good点取dp[v][0]
        dp[u][1].s += dp[v][0].s;
        // 当 u为非good点的时候,v可以为good 也可以为非good 取最优;
        int best = (dp[v][0] > dp[v][1])?0:1;
        dp[u][0].g += dp[v][best].g;
        dp[u][0].s += dp[v][best].s;
    }
}

int w[200005];

// 再次遍历更新点的权值
void dfs2(int u,int fa,int bes){
    // good 就取相连点个数 否则取1;
    w[u] = (bes)?g[u].size():1;
    
    for(auto v:g[u]){
        if(v == fa) continue;
        // 当前节点为good那么相邻节点就为非good
        if(bes) dfs2(v,u,0);
        else{
            int best = (dp[v][0] > dp[v][1])?0:1;
            dfs2(v,u,best);
        }
    }
}
signed main()
{
    cin >> n;
    forr(i,1,n-1){
        int l,r;cin >> l >> r;
        g[l].push_back(r);
        g[r].push_back(l);
    }
    //特判2
    if(n == 2){
        cout << 2 <<" " << 2 << endl;
        cout << 1 <<" "<< 1  << endl;
        return 0;
    }
    dfs1(1,0);
    int best = (dp[1][0] > dp[1][1])?0:1;
    //求每个点的权值
    dfs2(1,0,best);
    
    // 输出1节点最优情况
    cout << dp[1][best].g <<" "<<dp[1][best].s<<endl;
    forr(i,1,n) cout << w[i] <<" \n"[i==n];
    return 0;
}
 
举报

相关推荐

0 条评论