题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2427
今天的考试题...好不容易一次写对了树形DP,却没发现有环的情况...
发现自己 tarjan 都不太熟了,差点没写上那个 vis 数组!
还有点要注意的地方,如果一个环跟外部都不连边,要专门把它连到0点上去;
然后就是普通的树形DP了。
代码如下:
#include<iostream> #include<cstdio> #include<cstring> using namespace std; int n,m,w[105],v[105],head[105],ct,f[105][505],siz[105],ans,cr,col[105],hd[105],cnt; int dfn[105],low[105],tim,sta[105],top,vv[105],ww[105]; bool vis[105],in[105]; struct N{ int to,next; N(int t=0,int n=0):to(t),next(n) {} }edge[105],ed[105]; void dp(int x) { siz[x]=ww[x]; f[x][ww[x]]=vv[x]; f[x][0]=0; // cout<<x<<endl; // printf("%d head=%d ",x,head[x]); for(int j=hd[x],u;j;j=ed[j].next) { dp(u=ed[j].to); siz[x]+=siz[u]; for(int i=min(m,siz[x]);i>=ww[x];i--) for(int k=min(i-ww[x],siz[u]);k>=0;k--) // { f[x][i]=max(f[x][i],f[u][k]+f[x][i-k]); // if(f[x][i]>=0)printf("u=%d i=%d k=%d f[%d][%d]=%d ",u,i,k,x,i,f[x][i]); // } } // printf("return:%d ",x); } void tarjan(int x) { dfn[x]=low[x]=++tim; sta[++top]=x; vis[x]=1;// for(int i=head[x];i;i=edge[i].next) { int u=edge[i].to; if(!dfn[u]) { tarjan(u);low[x]=min(low[x],low[u]); } else if(vis[u])// low[x]=min(low[x],dfn[u]); } if(low[x]==dfn[x]) { cr++; while(sta[top]!=x) { col[sta[top]]=cr; ww[cr]+=w[sta[top]]; vv[cr]+=v[sta[top]]; vis[sta[top]]=0; top--; } ww[cr]+=w[x]; vv[cr]+=v[x]; col[x]=cr; vis[x]=0; top--; } } int main() { // freopen("software.in","r",stdin); // freopen("software.out","w",stdout); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&w[i]); for(int i=1;i<=n;i++)scanf("%d",&v[i]); for(int i=1,x;i<=n;i++) { scanf("%d",&x); edge[++ct]=N(i,head[x]); head[x]=ct; } for(int i=1;i<=n;i++) if(!dfn[i])tarjan(i); for(int i=0;i<=n;i++)// for(int j=head[i];j;j=edge[j].next) { int u=edge[j].to; if(col[i]==col[u])continue;// ed[++cnt]=N(col[u],hd[col[i]]); hd[col[i]]=cnt; in[col[u]]=1; } memset(f,-3,sizeof f); for(int i=1;i<=cr;i++) if(in[i]==0){ed[++cnt]=N(i,hd[0]); hd[0]=cnt;}//注意这部分! dp(0); for(int i=0;i<=m;i++)ans=max(ans,f[0][i]); printf("%d",ans); return 0; }