树型DP ZOJ3527 8月月赛1008 一图N个点,N条边,取某个点会有信任点,同时某个点与它的后继结点同时取的话,它的信任点会改变一个值,问怎么取点,使得总信任点最大

/*题意: 一图N个点,N条边,取某个点会有信任点,同时某个点与其它的后继结点同时取的话,
它的信任点会改变一个值,问怎么取点,使得总信任点最大 
分析:因为只有N个点,N条边,所以肯定是几个环(M个点M条边)加上一些尾巴,
对于环外的点,不断向上缩,有dp[i][0]+=Max(dp[soni][0],dp[soni][1]);
dp[i][1]+=Max(dp[soni][0],dp[soni][1]+g[soni]); 而对于每个环,枚举其中一个点的状态,
就变成一条链了,也可以运用上面的转移方程了 */
#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;

const int maxn=210000;
long long in[maxn],f[maxn],g[maxn],next[maxn],q[maxn];
long long dp[maxn][2],dp1[maxn][2];
bool flag[maxn];

long long Max(long long a,long long b)
{
	return a>b? a:b;
}
long long DFS1(long long f,long long now,long long dp[][2],long long flag)
{
	long long i,j,k;
	k=now;
	i=next[k];
	while(i!=f)//由k更新i,所以i到f那就不必了 
	{
		dp[i][0]+=Max(dp[k][0],dp[k][1]);
		dp[i][1]+=Max(dp[k][0],dp[k][1]+g[k]);
		k=next[k];
		i=next[k];
	}

    //退出时i(f)为k的下一个结点 
	if(flag) dp[k][1]+=g[k];//如果f和k同时取的话
	return Max(dp[k][0],dp[k][1]);
}
long long DFS(long long now)//含now的环 
{
    long long i,j,jj,k;
	k=now;
	i=next[k];
	dp1[i][0]+=dp[k][0];//now不取 
	dp1[i][1]+=dp[k][0];
	j=DFS1(now,i,dp1,0);

	dp[i][0]+=dp[k][1];//now取 
	dp[i][1]+=dp[k][1]+g[k];
	jj=DFS1(now,i,dp,1);
	return Max(j,jj);//两个选择中较优的 
}
int main()
{
	long long n,i,j,k,top,ii,jj;
	long long sum;
	while(scanf("%lld",&n)!=EOF)
	{
		memset(in,0,sizeof(in));
		for(i=1;i<=n;i++)
		{
			scanf("%lld%lld%lld",&f[i],&g[i],&next[i]);
			in[next[i]]++;//入度 
		}

		memset(dp,0,sizeof(dp));
		memset(flag,false,sizeof(flag));
		for(top=0,i=1;i<=n;i++)
		{
            dp[i][1]=f[i];//取的话肯定有f[i] 
			if(!in[i])//入度为0,就说明是尾巴,要往上缩 
				q[top++]=i;
        }

		while(top)//将所有环外的点都算出来 
		{
			k=q[--top];
			
			flag[k]=true;
			i=next[k];

			dp[i][0]+=Max(dp[k][0],dp[k][1]);
			dp[i][1]+=Max(dp[k][0],dp[k][1]+g[k]);
		
			if(--in[i]==0LL)
				q[top++]=i;
		}
		
		memcpy(dp1,dp,sizeof(dp));//因为要枚举一个环上的点的状态,所以这备份一个 
		for(sum=0,i=1;i<=n;i++)
		{
			if(!flag[i])//说明还有入度,即是环上的点 
			{
				sum+=DFS(i);//加上这个环的最优解 
				flag[i]=true;//标记这个环上的点 
				for(j=next[i];j!=i;j=next[j])
				   flag[j]=true;
            }
         }
		
		printf("%lld\n",sum);
	}
	return 0;
}


 

你可能感兴趣的:(树型DP ZOJ3527 8月月赛1008 一图N个点,N条边,取某个点会有信任点,同时某个点与它的后继结点同时取的话,它的信任点会改变一个值,问怎么取点,使得总信任点最大)