树形DP

P2607 [ZJOI2008] 骑士

每个骑士只有一个讨厌的人,所以反向建图后,就变成了每个点只有一条入边的外向树;

然后找环,拆环,因为环上的相邻两点只能选一个,所以我们需要强制其中一个不选;

答案为环上的点的最大值的和;

//和树形DP模板题【上司的舞会】很相似

AC Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e6+5;
const int inf=0x3f3f3f3f;

ll n,a[N];

inline ll read()
{
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}
//建图 
ll h[N],ver[N*2],nex[N*2],tot=0;
void add(int u,int v)
{
	ver[++tot]=v,nex[tot]=h[u],h[u]=tot;
}

int fa[N],hate[N],root,rt;
ll f[N][2];//f[i][0]不选i,f[i][1]选i

int find(int x)
{
	if(fa[x]==x)return x;
	return fa[x]=find(fa[x]);
}

void dfs(int x)//dp 
{
	f[x][1]=a[x],f[x][0]=0;
	if(rt==x)f[x][1]=-inf;
	for(int i=h[x];i;i=nex[i])
	{
		int v=ver[i];
		if(v==root)continue;
		dfs(v);
		if(x==rt)f[x][0]+=max(f[v][0],f[v][1]);
		else
		{
			f[x][0]+=max(f[v][0],f[v][1]);
			f[x][1]+=f[v][0];
		}
	}
}

void solve()
{
	long long ans=0;
	for(int i=1;i<=n;i++)
	{
		if(find(i)==i)//拆环,单一的树 
		{
			root=i;
			rt=hate[i]; 
			dfs(i);//强制不选hate[i] 
			ll t=max(f[i][1],f[i][0]);
			rt=i;
			dfs(i);//强制不选i 
			ans+=max(t,f[i][0]);
		}
	}
	cout<<ans<<endl;
}

int main()
{
	n=read();
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=n;i++)
	{
		a[i]=read();hate[i]=read();//只有一条入边(反向建图) 
		add(hate[i],i);fa[i]=find(hate[i]);//外向树 
	}
	solve();
	return 0;
}

P3621 [APIO2007] 风铃

这道题只有树形,没有dp,用dfs判断一下就好了,但是有很多细节;

  1. 先dfs一遍,记录玩具到根节点的最小距离和最远距离,如果差值大于1,不满足条件1,输出-1;

  2. 再dfs一遍,如果左子树最大值大于右子树最大值,交换左子树和右子树,ans++;

  3. 交换完了之后判断是否符合条件2,如果不符合结束dfs,输出-1,否则继续dfs;

  4. 最后输出ans;

AC Code
#include<bits/stdc++.h>
using namespace std;

#define ll long long
const int N=1e5+5;

inline ll read()
{
	ll x=0,f=1;
	char ch=getchar();
	while(ch<'0'||ch>'9')
	{
		if(ch=='-')f=-1;
		ch=getchar();
	}
	while(ch>='0'&&ch<='9')
	{
		x=x*10+ch-'0';
		ch=getchar();
	}
	return x*f;
}

int n;
struct ww{
	int l,r;
	int mx,mi;
}pl[N];

int fa[N];

int find(int x)
{
	if(fa[x]==x)return x;
	return fa[x]=find(fa[x]);
}

int d[N],ans=0;

void dfs(int x)
{
	if(x==-1)
	{
		pl[x].mi=pl[x].mx=1;
		return ;
	}
	dfs(pl[x].l);dfs(pl[x].r);
	pl[x].mx=max(pl[pl[x].l].mx,pl[pl[x].r].mx)+1;
	pl[x].mi=min(pl[pl[x].l].mi,pl[pl[x].r].mi)+1;
}

bool check(int x)
{
	if(x==-1)return true;
	if(check(pl[x].l)==0||check(pl[x].r)==0)return false;
	if(pl[pl[x].l].mx<pl[pl[x].r].mx)
	{
		swap(pl[x].l,pl[x].r);
		ans++;
	}
	if(pl[pl[x].l].mx==pl[pl[x].r].mx)
	{
		if(pl[pl[x].l].mi==pl[pl[x].r].mx)
		{
			return true;
		}
		else if(pl[pl[x].r].mi==pl[pl[x].r].mx)
		{
			swap(pl[x].l,pl[x].r);
			ans++;
			return true;
		}
		else return false;
	}
	if(pl[pl[x].l].mi<pl[pl[x].r].mx)return false;
	return true;
}

void solve()
{
	for(int i=1;i<=n;i++)
	{
		if(find(i)==i)
		{
			dfs(i);
			if(pl[i].mx>pl[i].mi+1)
			{
				printf("-1");
			}
			else if(check(i))
			{
				printf("%d",ans);
			}
			else printf("-1");
		}
	}
}

int main()
{
	n=read();
	for(int i=1;i<=n;i++)fa[i]=i;
	for(int i=1;i<=n;i++)
	{
		pl[i].l=read(),pl[i].r=read();
		if(pl[i].l!=-1)fa[pl[i].l]=find(i);
		if(pl[i].r!=-1)fa[pl[i].r]=find(i);
	}
	solve();
	return 0;
}
posted @ 2022-07-16 16:34  两只风小鱼  阅读(33)  评论(0)    收藏  举报