背包是一种简单的DP,把它放在树上就不简单了。
树形背包初级的做法:
设f[i][j]是以i节点为根,体积为i的最大收益。 其实原来是f[u][i][j]表示以u为根,选取i个子树,体积为j的最大收益,但通过体积那一维的倒序循环(像01背包一样)可以在空间上优化为O(n^2),但时间上仍很庞大,为O(n^3)。
f[i][j]=max(f[i][j],f[i][j-k]+f[v][k]) (循环j倒序,k从0到j。)
答案为f[根][背包体积]
于是就有了优化:
设f[i][j]表示dfs序从i到tot,体积为j的最大价值。
先dfs一遍,计算每棵子树的siz【】,对每一个dfs序建立与原编号的映射,(id[++num]=x).
f[i][j]=max(f[i+siz[i]][j],f[i+1][j-w[i]]+val[i]) (循环i倒序)
答案为f[1]【背包体积】
还有别的优化,但是我不会。。。
以下是代码:
1 #include<bits/stdc++.h> 2 #define ll long long 3 #define RE register 4 using namespace std; 5 const int maxn=2200; 6 inline int R(){ 7 RE char b=getchar();int x=0,t=1; 8 while(b<'0'||b>'9'){if(b=='-') t=-1;b=getchar();} 9 while(b>='0'&&b<='9') {x=(x<<3)+(x<<1)+b-'0';b=getchar();} 10 return x*t; 11 } 12 struct Edge{ 13 int nxt,to; 14 }ed[maxn]; 15 int head[maxn],ecnt; 16 void addedge(int f,int to){ 17 ed[++ecnt].to=to; 18 ed[ecnt].nxt=head[f]; 19 head[f]=ecnt; 20 } 21 int n,m; 22 int w[maxn],v[maxn],d[maxn]; 23 int ww[maxn],vv[maxn],rd[maxn]; 24 int siz[maxn]; 25 int f[maxn][maxn]; 26 void dfs(int x){// 划重点 27 for(int i=ww[x];i<=m;i++) f[x][i]=vv[x]; 28 // f[x][ww[x]]=vv[x]; 29 for(int i=head[x];i;i=ed[i].nxt){ 30 int v=ed[i].to; 31 dfs(v); 32 for(int j=m-ww[x];j>=0;j--) for(int k=0;k<=j;k++){ 33 f[x][j+ww[x]]=max(f[x][j+ww[x]],f[x][j+ww[x]-k]+f[v][k]); 34 } 35 } 36 } 37 int dfn[maxn],low[maxn],num; 38 bool vis[maxn]; 39 int sta[maxn],tp; 40 int scc_num,scc[maxn]; 41 void tarjan(int x){ 42 sta[++tp]=x; 43 vis[x]=1; 44 dfn[x]=low[x]=++num; 45 for(int i=head[x];i;i=ed[i].nxt){ 46 int v=ed[i].to; 47 if(!dfn[v]){ 48 tarjan(v); 49 low[x]=min(low[x],low[v]); 50 } 51 else if(vis[v]) low[x]=min(low[x],dfn[v]); 52 } 53 if(low[x]==dfn[x]){ 54 scc_num++; 55 int tmp; 56 do{ 57 tmp=sta[tp--]; 58 vis[tmp]=0; 59 scc[tmp]=scc_num; 60 ww[scc_num]+=w[tmp]; 61 vv[scc_num]+=v[tmp]; 62 }while(tmp!=x); 63 } 64 } 65 int main(){ 66 n=R(),m=R(); 67 for(int i=1;i<=n;i++) w[i]=R(); 68 for(int i=1;i<=n;i++) v[i]=R(); 69 for(int i=1;i<=n;i++){ 70 d[i]=R(); 71 if(d[i]) addedge(d[i],i); 72 } 73 for(int i=1;i<=n;i++){ 74 if(!dfn[i]) tarjan(i); 75 } 76 memset(head,0,sizeof head); 77 memset(ed,0,sizeof ed);ecnt=0; 78 for(int i=1;i<=n;i++){ 79 int f=d[i],to=i; 80 if(scc[f]!=scc[to]&&f) addedge(scc[f],scc[to]),rd[scc[to]]++; 81 } 82 for(int i=1;i<=scc_num;i++) 83 if(!rd[i]) addedge(scc_num+1,i);//一定要先缩点再加超级源点,否则环是不会和超级源点连上边的 84 dfs(scc_num+1); 85 printf("%d\n",f[scc_num+1][m]); 86 return 0; 87 }
1 #include<bits/stdc++.h> 2 #define ll long long 3 #define RE register 4 using namespace std; 5 const int maxn=2200; 6 inline int R(){ 7 RE char b=getchar();int x=0,t=1; 8 while(b<'0'||b>'9'){if(b=='-') t=-1;b=getchar();} 9 while(b>='0'&&b<='9') {x=(x<<3)+(x<<1)+b-'0';b=getchar();} 10 return x*t; 11 } 12 struct Edge{ 13 int nxt,to; 14 }ed[maxn]; 15 int head[maxn],ecnt; 16 void addedge(int f,int to){ 17 ed[++ecnt].to=to; 18 ed[ecnt].nxt=head[f]; 19 head[f]=ecnt; 20 } 21 int n,m; 22 int w[maxn],v[maxn],d[maxn]; 23 int ww[maxn],vv[maxn],rd[maxn]; 24 int siz[maxn]; 25 int f[maxn][maxn]; 26 //void dfs(int x){ 27 // for(int i=ww[x];i<=m;i++) f[x][i]=vv[x]; 28 //// f[x][ww[x]]=vv[x]; 29 // for(int i=head[x];i;i=ed[i].nxt){ 30 // int v=ed[i].to; 31 // dfs(v); 32 // for(int j=m-ww[x];j>=0;j--) for(int k=0;k<=j;k++){ 33 // f[x][j+ww[x]]=max(f[x][j+ww[x]],f[x][j+ww[x]-k]+f[v][k]); 34 // } 35 // } 36 //} 37 int id[maxn],num; 38 void dfs(int x){ 39 id[++num]=x;//划重点 40 siz[x]=1; 41 for(int i=head[x];i;i=ed[i].nxt){ 42 int v=ed[i].to; 43 dfs(v); 44 siz[x]+=siz[v]; 45 } 46 } 47 int dfn[maxn],low[maxn]; 48 bool vis[maxn]; 49 int sta[maxn],tp; 50 int scc_num,scc[maxn]; 51 void tarjan(int x){ 52 sta[++tp]=x; 53 vis[x]=1; 54 dfn[x]=low[x]=++num; 55 for(int i=head[x];i;i=ed[i].nxt){ 56 int v=ed[i].to; 57 if(!dfn[v]){ 58 tarjan(v); 59 low[x]=min(low[x],low[v]); 60 } 61 else if(vis[v]) low[x]=min(low[x],dfn[v]); 62 } 63 if(low[x]==dfn[x]){ 64 scc_num++; 65 int tmp; 66 do{ 67 tmp=sta[tp--]; 68 vis[tmp]=0; 69 scc[tmp]=scc_num; 70 ww[scc_num]+=w[tmp]; 71 vv[scc_num]+=v[tmp]; 72 }while(tmp!=x); 73 } 74 } 75 int main(){ 76 n=R(),m=R(); 77 for(int i=1;i<=n;i++) w[i]=R(); 78 for(int i=1;i<=n;i++) v[i]=R(); 79 for(int i=1;i<=n;i++){ 80 d[i]=R(); 81 if(d[i]) addedge(d[i],i); 82 } 83 for(int i=1;i<=n;i++){ 84 if(!dfn[i]) tarjan(i); 85 } 86 memset(head,0,sizeof head); 87 memset(ed,0,sizeof ed);ecnt=0; 88 for(int i=1;i<=n;i++){ 89 int f=d[i],to=i; 90 if(scc[f]!=scc[to]&&f) addedge(scc[f],scc[to]),rd[scc[to]]++; 91 } 92 for(int i=1;i<=scc_num;i++) 93 if(!rd[i]) addedge(scc_num+1,i);//一定要先缩点再加超级源点,否则环是不会和超级源点连上边的 94 memset(dfn,0,sizeof dfn);num=0; 95 dfs(scc_num+1); 96 for(int i=num;i;i--){//划重点 97 for(int j=0;j<=m;j++) 98 if(j-ww[id[i]]>=0) f[i][j]=max(f[i+siz[id[i]]][j],f[i+1][j-ww[id[i]]]+vv[id[i]]);//划重点 99 else f[i][j]=f[i+siz[id[i]]][j];//划重点——————尤其重点 100 } 101 printf("%d\n",f[1][m]);//划重点 102 return 0; 103 }
这道题让我调了一下午的原因竟是……………………Tarjan写错了。。