树形DP+背包
老师让我们补做了一下PKUSC那周的题目= =
这次好像是树形DP的专题?感觉题目还是很棒的,值得将来再回头学习。
Cateran
树形状压DP,其实在看题解之前我似乎并没有搞懂这题在干什么……
对于节点 i ,我们考虑f[i][j]表示 i 这棵子树中,分部包含情况为 j 的最大收益,因为一个分部管的是从x到根的所有点,所以对于一个点来说,管它的就是子树中的所有分部,所以就可以dp啦~依次枚举每个儿子的子树中有哪些分部,用一个辅助数组,我们可以搞:
c[j]=f[x][j];
c[j]=max(c[j],f[y][k]+f[x][j^k]);
f[x][j]=c[j];
依次搞下来即可。
初始化就是分部全部在 x 节点。计算完后要加上符合的收益(T种中的某一些……)
1 //20150527 cateran 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<iostream> 6 #include<algorithm> 7 #define rep(i,n) for(int i=0;i<n;++i) 8 #define F(i,j,n) for(int i=j;i<=n;++i) 9 #define D(i,j,n) for(int i=j;i>=n;--i) 10 #define pb push_back 11 using namespace std; 12 typedef long long LL; 13 inline int getint(){ 14 int r=1,v=0; char ch=getchar(); 15 for(;!isdigit(ch);ch=getchar()) if (ch==‘-‘) r=-1; 16 for(; isdigit(ch);ch=getchar()) v=v*10-‘0‘+ch; 17 return r*v; 18 } 19 const int N=4100; 20 /*******************template********************/ 21 22 int head[110],nxt[220],to[220],cnt; 23 void add(int x,int y){ 24 to[++cnt]=y; nxt[cnt]=head[x]; head[x]=cnt; 25 to[++cnt]=x; nxt[cnt]=head[y]; head[y]=cnt; 26 } 27 int n,m,T,a[110][15],b[N],c[N]; 28 int f[110][N]; 29 30 void dfs(int x,int fa){ 31 rep(j,(1<<m)){ 32 f[x][j]=0; 33 rep(i,m) if (j&(1<<i)) f[x][j]-=a[x][i+1]; 34 // printf("f[%d][%d]=%d\n",x,j,f[x][j]); 35 } 36 int y; 37 for(int i=head[x];i;i=nxt[i]) 38 if ((y=to[i])!=fa){ 39 dfs(y,x); 40 rep(j,(1<<m)) c[j]=f[x][j]; 41 rep(j,(1<<m)) 42 for(int k=j;k;k=(k-1)&j) 43 c[j]=max(c[j],f[y][k]+f[x][j^k]); 44 rep(j,1<<m) f[x][j]=c[j]; 45 } 46 rep(i,1<<m) 47 for(int k=i;k;k=(k-1)&i) f[x][i]+=b[k]; 48 // rep(i,1<<m) printf("f[%d][%d]=%d\n",x,i,f[x][i]); 49 } 50 int main(){ 51 #ifndef ONLINE_JUDGE 52 freopen("cateran.in","r",stdin); 53 freopen("cateran.out","w",stdout); 54 #endif 55 n=getint(); m=getint(); 56 F(i,2,n){ 57 int x=getint(),y=getint(); 58 add(x,y); 59 } 60 F(i,1,n) F(j,1,m) a[i][j]=getint(); 61 T=getint(); 62 F(i,1,T){ 63 int v=getint(),c=getint(),now=0,x; 64 F(j,1,c){ 65 x=getint(); 66 now|=1<<(x-1); 67 } 68 b[now]+=v; 69 } 70 dfs(1,0); 71 printf("%d\n",f[1][(1<<m)-1]); 72 return 0; 73 }
Party
……傻逼题吧……f[x][0]表示邀请这个人时,这棵子树的最大收益;f[x][1]表示不邀请……
1 //20150527 party 2 #include<cstdio> 3 #include<string> 4 #include<map> 5 #include<cstring> 6 #include<cstdlib> 7 #include<iostream> 8 #include<algorithm> 9 #define rep(i,n) for(int i=0;i<n;++i) 10 #define F(i,j,n) for(int i=j;i<=n;++i) 11 #define D(i,j,n) for(int i=j;i>=n;--i) 12 #define pb push_back 13 using namespace std; 14 typedef long long LL; 15 inline int getint(){ 16 int r=1,v=0; char ch=getchar(); 17 for(;!isdigit(ch);ch=getchar()) if (ch==‘-‘) r=-1; 18 for(; isdigit(ch);ch=getchar()) v=v*10-‘0‘+ch; 19 return r*v; 20 } 21 const int N=110; 22 /*******************template********************/ 23 24 int head[N],to[N],next[N],cnt; 25 void add(int x,int y){ 26 to[++cnt]=y; next[cnt]=head[x]; head[x]=cnt; 27 } 28 29 int v[N],fa[N],f[N][2],n; 30 char s1[N][25],s2[N][25]; 31 map<string,int>mp; 32 33 void dfs(int x){ 34 f[x][0]=0; f[x][1]=v[x]; 35 for(int i=head[x];i;i=next[i]){ 36 dfs(to[i]); 37 f[x][0]+=max(f[to[i]][0],f[to[i]][1]); 38 f[x][1]+=f[to[i]][0]; 39 } 40 } 41 int main(){ 42 #ifndef ONLINE_JUDGE 43 freopen("party.in","r",stdin); 44 freopen("party.out","w",stdout); 45 #endif 46 // ios::sync_with_stdio(false); 47 cin >> n; 48 F(i,1,n){ 49 cin >> s1[i] >> v[i] >> s2[i]; 50 mp[s1[i]]=i; 51 } 52 F(i,1,n){ 53 fa[i]=mp.find(s2[i])->second; 54 add(fa[i],i); 55 } 56 // F(i,1,n) printf("%d ",fa[i]); puts(""); 57 int ans=0; 58 F(i,1,n) if (fa[i]==0){ 59 dfs(i); 60 ans+=max(f[i][0],f[i][1]); 61 } 62 printf("%d\n",ans); 63 return 0; 64 }
Railway
无向图最大权路径覆盖= =?然而其实并不是费用流之类……
f[x][0]表示x是孤立点时,x子树内最大收益;
f[x][1]表示x子树内有一条链与它相连;
f[x][2]表示x子树内有两条链与它相连
前两种情况是可以继续向上连的,而第三种情况就不可以了……
转移……Orz regina8023 其实是个贪心。
先假设所有点都没跟x连,得到f[x][0],然后再考虑一下将某个儿子连到父亲,会使答案改变多少,贪心地选出1-2个,即可得到f[x][1]和f[x][2]。
1 //20150527 railway 2 #include<cstdio> 3 #include<queue> 4 #include<cstring> 5 #include<cstdlib> 6 #include<iostream> 7 #include<algorithm> 8 #define rep(i,n) for(int i=0;i<n;++i) 9 #define F(i,j,n) for(int i=j;i<=n;++i) 10 #define D(i,j,n) for(int i=j;i>=n;--i) 11 #define pb push_back 12 using namespace std; 13 typedef long long LL; 14 inline int getint(){ 15 int r=1,v=0; char ch=getchar(); 16 for(;!isdigit(ch);ch=getchar()) if (ch==‘-‘) r=-1; 17 for(; isdigit(ch);ch=getchar()) v=v*10-‘0‘+ch; 18 return r*v; 19 } 20 const int N=2010,M=4010,INF=0x3f3f3f3f; 21 /*******************template********************/ 22 23 int head[N],nxt[M],to[M],l[M],cnt; 24 void add(int x,int y,int z){ 25 to[++cnt]=y; nxt[cnt]=head[x]; head[x]=cnt; l[cnt]=z; 26 to[++cnt]=x; nxt[cnt]=head[y]; head[y]=cnt; l[cnt]=z; 27 } 28 29 int n,ans; 30 31 int f[N][3],fa[N]; 32 void dfs(int x){ 33 f[x][0]=f[x][1]=0; 34 int max1=-INF,max2=-INF; 35 for(int i=head[x];i;i=nxt[i]) 36 if (to[i]!=fa[x]){ 37 fa[to[i]]=x; 38 dfs(to[i]); 39 int t=max(f[to[i]][1],f[to[i]][2]); 40 f[x][0]+=t; 41 f[x][1]+=t; 42 f[x][2]+=t; 43 int p=l[i]+max(f[to[i]][0],f[to[i]][1])-t; 44 if (p>max1) max2=max1,max1=p; 45 else if (p>max2) max2=p; 46 } 47 f[x][1]+=max1; 48 f[x][2]+=max1+max2; 49 } 50 51 int main(){ 52 #ifndef ONLINE_JUDGE 53 freopen("railway.in","r",stdin); 54 freopen("railway.out","w",stdout); 55 #endif 56 n=getint(); 57 F(i,2,n){ 58 int x=getint(),y=getint(),z=getint(); 59 add(x,y,z); 60 } 61 dfs(1); 62 printf("%d\n",max(f[1][1],f[1][2])); 63 return 0; 64 }
Lamp
树形背包。
好像是在背包九讲的泛型背包里讲的……
反正也是很easy的一道题……记录一下从哪里转移过来的即可
f[x][i]表示以x为根的子树中总重量为 i 时的最大亮度和。
g[x][i]表示以x为根的子树中,不考虑根,总重量为 i 时的最大亮度和。
所以我们有:
g[x][i]=max(g[x][i],g[x][j]+f[son][i-j])
f[x][i]=g[x][i-w[x]]+l[x];
容易发现其实用一个数组就可以了= =这里我用两个数组来表示,只是为了方便理解。
1 //20150527 lamp 2 #include<cstdio> 3 #include<cstring> 4 #include<cstdlib> 5 #include<iostream> 6 #include<algorithm> 7 #define rep(i,n) for(int i=0;i<n;++i) 8 #define F(i,j,n) for(int i=j;i<=n;++i) 9 #define D(i,j,n) for(int i=j;i>=n;--i) 10 #define pb push_back 11 using namespace std; 12 typedef long long LL; 13 inline int getint(){ 14 int r=1,v=0; char ch=getchar(); 15 for(;!isdigit(ch);ch=getchar()) if (ch==‘-‘) r=-1; 16 for(; isdigit(ch);ch=getchar()) v=v*10-‘0‘+ch; 17 return r*v; 18 } 19 const int N=410; 20 /*******************template********************/ 21 22 int head[N],nxt[N],to[N],cnt; 23 void add(int x,int y){ 24 to[++cnt]=y; nxt[cnt]=head[x]; head[x]=cnt; 25 } 26 int f[N][N],w[N],p[N],l[N],n,fa[N]; 27 bool yes[N]; 28 typedef pair<int,int> pii; 29 #define mp make_pair 30 pii from[N][N]; 31 32 void dfs(int x){ 33 for(int i=head[x];i;i=nxt[i]){ 34 dfs(to[i]); 35 D(j,p[x],w[to[i]]) F(k,w[to[i]],j) 36 if (f[x][j-k]+f[to[i]][k]>f[x][j]){ 37 from[x][j]=mp(to[i],k); 38 f[x][j]=f[x][j-k]+f[to[i]][k]; 39 } 40 } 41 if (x!=1){ 42 D(i,p[x]+w[x],w[x]){ 43 f[x][i]=f[x][i-w[x]]+l[x]; 44 from[x][i]=from[x][i-w[x]]; 45 } 46 } 47 } 48 void Find(int x,int y){ 49 if (!x || !y) return; 50 // cout <<"Find"<<x <<" "<<y <<endl; 51 yes[x]=1; 52 Find(from[x][y].first,from[x][y].second); 53 if (from[x][y].second) Find(x,y-from[x][y].second); 54 } 55 int main(){ 56 #ifndef ONLINE_JUDGE 57 freopen("lamp.in","r",stdin); 58 freopen("lamp.out","w",stdout); 59 #endif 60 n=getint(); 61 F(i,1,n){ 62 fa[i]=getint(),w[i]=getint(),p[i]=getint(),l[i]=getint(); 63 add(fa[i],i); 64 } 65 dfs(1); 66 Find(1,p[1]); 67 int num=0; 68 F(i,1,n) num+=yes[i]; 69 printf("%d ",num); 70 printf("%d\n",f[1][p[1]]+l[1]); 71 int cnt=0; 72 F(i,1,n) if (yes[i]) 73 printf("%d%c",i,(++cnt)==num ? ‘\n‘ : ‘ ‘); 74 // F(i,1,n){F(j,1,p[i]) printf("%d ",f[i][j]); puts("");} 75 // F(i,1,n)F(j,1,p[i]+w[i])if (from[i][j].second) printf("from[%d][%d]=%d,%d\n",i,j,from[i][j].first,from[i][j].second); 76 return 0; 77 }