ZOJ3527
题意:给你一个有向图,一共N个顶点,且每个顶点只有一个前驱或后继,在顶点上建立圣地,那么就可以获得一个信仰值,如果在这个顶点的后继节点上也建立圣地,那么将改变一定的信仰值,求解能获取的最大信仰值。
思路:好吧,这题一点思路都没有,本来题目就做的比别人少,树状DP什么的,更是少,这次要不是准备好好研究下DP问题,估计想都没想过去接触这种东西,只怪自己太懒了。题目是求最大值的,所以考虑要不要在这个点上新建圣地问题,那么就分两种情况了,所以取其中最大的值就行,由于题目给的是简单环,所以之前要进行处理下,n个顶点,n条边,所有先处理环上的树枝节点,然后再对环做DP就可以。
思路:好吧,这题一点思路都没有,本来题目就做的比别人少,树状DP什么的,更是少,这次要不是准备好好研究下DP问题,估计想都没想过去接触这种东西,只怪自己太懒了。题目是求最大值的,所以考虑要不要在这个点上新建圣地问题,那么就分两种情况了,所以取其中最大的值就行,由于题目给的是简单环,所以之前要进行处理下,n个顶点,n条边,所有先处理环上的树枝节点,然后再对环做DP就可以。
#include<stdio.h> #include<string.h> #include<iostream> using namespace std; typedef long long LL; const int MAX=100010; LL in_degree[MAX],sec[MAX],g[MAX],f[MAX],q[MAX]; LL DP[MAX][2],re[MAX][2]; bool flag[MAX]; LL n,k; LL sum; int i,j; LL ss(LL f,LL now,LL DP[][2],LL flag) { LL i,j,k; k=now; i=sec[k]; while(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=sec[k]; i=sec[k]; } if(flag) DP[k][1]+=g[k]; return max(DP[k][0],DP[k][1]); } LL solve(LL now) { LL i,j,xx,k; k=now; i=sec[k]; re[i][0]+=DP[k][0]; re[i][1]+=DP[k][0]; j=ss(now,i,re,0); DP[i][0]+=DP[k][1]; DP[i][1]+=DP[k][1]+g[k]; xx=ss(now,i,DP,1); return max(j,xx); } void branch() { int x; for(x=0,i=1; i<=n; i++) { DP[i][1]=f[i]; if(!in_degree[i]) q[x++]=i; } while(x) { k=q[--x]; flag[k]=true; i=sec[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_degree[i]==0LL) q[x++]=i; } } LL ring() { memcpy(re,DP,sizeof(DP)); for(sum=0,i=1; i<=n; i++) { if(!flag[i]) { sum+=solve(i); flag[i]=true; for(j=sec[i]; j!=i; j=sec[j]) flag[j]=true; } } return sum; } int main() { while(scanf("%lld",&n)!=EOF) { memset(in_degree,0,sizeof(in_degree)); memset(DP,0,sizeof(DP)); memset(flag,false,sizeof(flag)); for(i=1; i<=n; i++) { scanf("%lld%lld%lld",&f[i],&g[i],&sec[i]); in_degree[sec[i]]++; } branch(); //将树枝节点分离并且计算 printf("%lld\n",ring()); //另外对环上的所有点DP } return 0; }