树形DP(2)
一、P2656 采蘑菇
#include<cstring> #include<cstdio> #include<algorithm> #include<iostream> #include<cmath> using namespace std; #define maxn 80010 #define maxm 300010 struct Edge{ int u,v,w,next; double k; }edge[maxm]; int head[maxn],instack[maxn],stack[maxn],Belong[maxn]; int DFN[maxn],LOW[maxn],n,m,ant,Index,top,Bcnt; int point[maxn],S,ans[maxn]; void adde(int u,int v,int w,double k){ edge[ant].u=u,edge[ant].v=v; edge[ant].w=w,edge[ant].k=k; edge[ant].next=head[u],head[u]=ant++; } void tarjan(int u){ DFN[u]=LOW[u]=++Index; instack[u]=true; stack[++top]=u; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; if(!DFN[v]){ tarjan(v); if(LOW[v]<LOW[u]) LOW[u]=LOW[v]; } else{ if(instack[v]&&DFN[v]<LOW[u]) LOW[u]=DFN[v]; } } if(DFN[u]==LOW[u]){ Bcnt++; int j; do{ j=stack[top--]; instack[j]=false; Belong[j]=Bcnt; } while(j!=u); } } void solve(){ top=Bcnt=Index=0; for(int i=1;i<=n;i++){ if(!DFN[i]) tarjan(i); } } void dfs(int p){ ans[p]+=point[p]; int t=0; for(int i=head[p];i!=-1;i=edge[i].next){ int v=edge[i].v,w=edge[i].w; if(!ans[v]) dfs(v); t=max(t,ans[v]+w); } ans[p]+=t; } int main(){ memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); for(int i=1;i<=m;i++){ int u,v,w; double k; scanf("%d%d%d%lf",&u,&v,&w,&k); adde(u,v,w,k); } scanf("%d",&S); solve(); memset(head,-1,sizeof(head)); for(int i=0;i<m;i++){ int u=edge[i].u,v=edge[i].v; int w=edge[i].w; double k=edge[i].k; if(Belong[u]!=Belong[v]){ adde(Belong[u],Belong[v],w,k); }else{ while(w){ point[Belong[u]]+=w; w=floor(w*k); } } } dfs(Belong[S]); printf("%d\n",ans[Belong[S]]); return 0; }
缩点模板题,dfs一开始还写错了。
二、P1040 加分二叉树
#include<cstring> #include<cstdio> #include<algorithm> #include<iostream> #include<cmath> using namespace std; #define maxn 35 long long w[maxn],n,dp[maxn][maxn],root[maxn][maxn]; void myprint(int l,int r){ if(l>r) return; printf("%d ",root[l][r]); myprint(l,root[l][r]-1); myprint(root[l][r]+1,r); } int main(){ scanf("%d",&n); for(int i=1;i<=n;i++) scanf("%lld",&w[i]); for(int i=0;i<=n;i++){ dp[i+1][i]=1; root[i][i]=i; dp[i][i]=w[i]; } for(int len=2;len<=n;len++){ for(int i=1;i<n;i++){ int j=i+len-1; for(int k=i;k<=j;k++){ if(dp[i][j]<dp[i][k-1]*dp[k+1][j]+w[k]){ dp[i][j]=dp[i][k-1]*dp[k+1][j]+w[k]; root[i][j]=k; } } } } printf("%lld\n",dp[1][n]); myprint(1,n); return 0; }
如果现在给出一个中序遍历的序列,并且给出你所有子树根节点 ,123456789(8是整个树的根,4是子树[1,6]的根(用区间表示子树),2是树[1,6]的子树[1,3]的根,而5是树[1,6]的子树[5,6]的根)。
root数组看的题解,有点妙!
三、P1122 最大子树和
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define maxn 16010 int N,w[maxn],head[maxn],ant,dp[maxn],vis[maxn]; struct Egde{ int v,next; }edge[maxn*2]; void adde(int u,int v){ edge[ant].v=v; edge[ant].next=head[u],head[u]=ant++; } void dfs(int p){ dp[p]+=w[p]; vis[p]=1; for(int i=head[p];i!=-1;i=edge[i].next){ int v=edge[i].v; if(!vis[v]){ dfs(v); if(dp[v]>0) dp[p]+=dp[v]; } } } int main(){ memset(head,-1,sizeof(head)); scanf("%d",&N); for(int i=1;i<=N;i++) scanf("%d",&w[i]); for(int i=1;i<N;i++){ int u,v; scanf("%d%d",&u,&v); adde(u,v); adde(v,u); } dfs(1); int ans=0; for(int i=1;i<=N;i++){ ans=max(ans,dp[i]); } printf("%d\n",ans); return 0; }
比较简单
四、P2585 [ZJOI2006]三色二叉树
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define maxn 500010 int head[maxn],ant,ans,dp1[maxn][3],dp2[maxn][3],p; char s[maxn]; struct Egde{ int v,next; }edge[maxn*2]; void adde(int u,int v){ edge[ant].v=v; edge[ant].next=head[u],head[u]=ant++; } void dfs(int u){ if(s[p]=='1'){ adde(u,ans); ++p; dfs(ans++); } else if(s[p]=='0') return; else if(s[p]=='2'){ adde(u,ans); ++p; dfs(ans++); adde(u,ans); ++p; dfs(ans++); } } void dfs2(int u,int p){ if(p==1) dp1[u][p]=dp2[u][p]=1; else dp1[u][p]=dp2[u][p]=0; int v1=0,v2=0; for(int i=head[u];i!=-1;i=edge[i].next){ int v=edge[i].v; if(!v1) v1=v; else v2=v; } if(!v1) return; if(!v2){ if(dp1[v1][(p+1)%3]==-1) dfs2(v1,(p+1)%3); if(dp1[v1][(p+2)%3]==-1) dfs2(v1,(p+2)%3); dp1[u][p]+=max(dp1[v1][(p+1)%3],dp1[v1][(p+2)%3]); dp2[u][p]+=min(dp2[v1][(p+1)%3],dp2[v1][(p+2)%3]); } else{ if(dp1[v1][(p+1)%3]==-1) dfs2(v1,(p+1)%3); if(dp1[v1][(p+2)%3]==-1) dfs2(v1,(p+2)%3); if(dp1[v2][(p+1)%3]==-1) dfs2(v2,(p+1)%3); if(dp1[v2][(p+2)%3]==-1) dfs2(v2,(p+2)%3); int ans1=max(dp1[v1][(p+1)%3]+dp1[v2][(p+2)%3],dp1[v1][(p+2)%3]+dp1[v2][(p+1)%3]); int ans2=min(dp2[v1][(p+1)%3]+dp2[v2][(p+2)%3],dp2[v1][(p+2)%3]+dp2[v2][(p+1)%3]); dp1[u][p]+=ans1; dp2[u][p]+=ans2; } } int main(){ ans=2; memset(head,-1,sizeof(head)); memset(dp1,-1,sizeof(dp1)); memset(dp2,-1,sizeof(dp2)); scanf("%s",s+1); int len=strlen(s+1); p=1; dfs(1); for(int i=0;i<3;i++) dfs2(1,i); int ans1=max(dp1[1][0],max(dp1[1][1],dp1[1][2])); int ans2=min(dp2[1][0],min(dp2[1][1],dp2[1][2])); printf("%d %d\n",ans1,ans2); return 0; }
比较简单
五、P1273 有线电视网
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> using namespace std; #define maxn 3010 #define inf 0x3f3f3f3f int n,m,ant,head[maxn],dp[maxn][maxn],vis[maxn],val[maxn],account[maxn]; struct Edge{ int v,next,w; }edge[maxn]; void adde(int u,int v,int w){ edge[ant].v=v,edge[ant].w=w; edge[ant].next=head[u],head[u]=ant++; } void dfs(int p){ if(val[p]!=-1) account[p]=1; for(int i=head[p];i!=-1;i=edge[i].next){ int v=edge[i].v; dfs(v); account[p]+=account[v]; } } void dfs2(int p){ vis[p]=1; if(val[p]!=-1){ dp[p][1]=val[p]; return; } int ans=0; for(int i=head[p];i!=-1;i=edge[i].next){ int v=edge[i].v,w=edge[i].w; ans+=account[v]; for(int j=ans;j>0;j--){ for(int k=account[v];k>=j+account[v]-ans&&k>0;k--){ if(!vis[v]) dfs2(v); //printf("%d %d %d %d %d %d %d",p,j,k,dp[p][j-k],w,v,dp[v][k]); dp[p][j]=max(dp[p][j],dp[p][j-k]-w+dp[v][k]); //printf(" %d\n",dp[p][j]); } } } } int main(){ scanf("%d%d",&n,&m); memset(val,-1,sizeof(val)); memset(dp,-inf,sizeof(dp)); for(int i=1;i<=n;i++) dp[i][0]=0; memset(head,-1,sizeof(head)); for(int i=1;i<=n-m;i++){ int p; scanf("%d",&p); while(p--){ int v,w; scanf("%d%d",&v,&w); adde(i,v,w); } } for(int i=n-m+1;i<=n;i++) scanf("%d",&val[i]); dfs(1); dfs2(1); for(int i=account[1];i>=0;i--) if(dp[1][i]>=0){ printf("%d\n",i); break; } return 0; }
一个特别好的题,但是自己没写出来,然后看了题解。
首先明确dp少什么,得有表示第几个点的i,第i点提供j个客户所赚的利润dp[i][j]...都是马后炮,还得再多练练。

浙公网安备 33010602011771号