有关树形背包

  背包是一种简单的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]【背包体积】

  还有别的优化,但是我不会。。。

   以下是代码:

     例题     luoguP2515 [HAOI2010]软件安装

 

 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 }
dfs序

这道题让我调了一下午的原因竟是……………………Tarjan写错了。。

转载于:https://www.cnblogs.com/sdfzjdx/p/11267590.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值