牛客周赛 Round 15 D 游游的树上边染红(树形dp)

牛客周赛 Round 15 D 游游的树上边染红(树形dp)

牛客周赛 Round 15 D 游游的树上边染红(树形dp)_第1张图片
一道很裸的树形dp,周日晚上看了一晚上看不懂,第二天突然就悟了。
题目跟没有上司从舞会很像,我们粗略的考虑,当前节点的状态为选/不选,然后根据此进行状态转移。

不选择当前点: d p [ u ] [ 0 ] dp[u][0] dp[u][0],表示我不选以节点 u 为上节点的边,那么对于 u 的所有子节点,我选择其状态的最大值即可。
即:
d p [ u ] [ 0 ] + = m a x ( d p [ v ] [ 0 ] , d p [ v ] [ 1 ] ) , v 为 u 的子节点 dp[u][0]+=max(dp[v][0],dp[v][1]),v为u的子节点 dp[u][0]+=max(dp[v][0],dp[v][1]),vu的子节点

选择当前点: d p [ u ] [ 1 ] dp[u][1] dp[u][1],表示我选择了以节点 u 为上节点的边,则需要去对 u 与其所有子节点的边进行遍历,取最大值即可。

d p [ x ] [ 1 ] = m a x ( d p [ x ] [ 1 ] , d p [ x ] [ 0 ] − m a x ( d p [ u ] [ 0 ] , d p [ u ] [ 1 ] ) + d p [ u ] [ 0 ] + w ) dp[x][1]=max(dp[x][1],dp[x][0]-max(dp[u][0],dp[u][1])+dp[u][0]+w) dp[x][1]=max(dp[x][1],dp[x][0]max(dp[u][0],dp[u][1])+dp[u][0]+w)

该方程的状态转移解释为:我若是选择了该边,那么对于子节点 v ,我只能取其不选的状态,即 d p [ v ] [ 0 ] dp[v][0] dp[v][0],而又因为我是选择的 u − v i u-v_i uvi这条边,所以不影响 u 对于其他边的选择,我只需要对其他子节点的状态依旧取 max 即可。

#include 

using namespace std;
const int N = 2e6 + 5;
typedef long long ll;
typedef pair<ll, ll> pll;
typedef array<ll, 3> p3;
int mod = 998244353;
const int maxv = 4e6 + 5;
// #define endl "\n"

int n;
vector<pll> e[N];

void add(int u,int v,int w)
{
	e[u].push_back({v,w});
	e[v].push_back({u,w});
}
ll dp[N][2];

void dfs(int x,int f)
{
	ll sum=0;
	for(auto [u,w]: e[x]){
		if(u!=f){
            dfs(u,x);
			dp[x][0]+=max(dp[u][0],dp[u][1]);
			//sum+=max(dp[u][0],dp[u][1]);
		}
	}
	for(auto [u,w]: e[x]){
        if(u!=f)
		dp[x][1]=max(dp[x][1],dp[x][0]-max(dp[u][0],dp[u][1])+dp[u][0]+w);
	}
    //cout<
}

void solve()
{	
	cin>>n;
	for(int i=1;i<=n-1;i++){
		ll u,v,w;
		cin>>u>>v>>w;
		add(u,v,w);
	}
	dfs(1,-1);
	cout<<max(dp[1][0],dp[1][1])<<endl;
}

int main()
{
    ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	int t;
	t=1;
	//cin>>t;
	while(t--){
		solve();
	}
    system("pause");
    return 0;
}

你可能感兴趣的:(#,树形dp,dp,深度优先,图论,算法)