题意:
给出一棵树,根节点为1。你可以给x个点赋值A,剩下n-x个点赋值B。询问从根节点到每个点的路径构成的字符串的最小集合。
题解:
结论1:每一层的点赋相同的值,可以使答案最优。
所以可以把每一层看成物品,物品的价值就是这一层的点数,跑一个01背包。
如果存在一组物品使得它们的和恰好为x,那么就直接输出层数即可。
如果不存在,最多也只会是层数+1。
结论2:对单层修改叶子节点的值,对答案的影响最小。
先找到叶子节点最多的那一层,试图通过改这一层的叶子节点的状态来弥补DP状态。
结果T15。
这是TLE的代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+100; int n,x; vector<int> g[maxn]; vector<int> d[maxn]; int cnt[maxn]; int m; void dfs (int x,int f,int dep) { m=max(m,dep); d[dep].push_back(x); if (g[x].size()==1) cnt[dep]++; for (int y:g[x]) { if (y==f) continue; dfs(y,x,dep+1); } } map<int,int> f[maxn]; int ans[maxn]; int ff=0; int main () { scanf("%d%d",&n,&x); if (x>n-x) x=n-x,ff=1; for (int i=2;i<=n;i++) { int y; scanf("%d",&y); g[i].push_back(y); g[y].push_back(i); } dfs(1,0,1); //for (int i=1;i<=m;i++) printf("%d ",d[i].size()); //rintf(" "); f[0][0]=1; for (int i=1;i<=m;i++) { for (int j=x;j>=0;j--) f[i][j]|=f[i-1][j]; for (int j=x;j>=d[i].size();j--) { f[i][j]|=f[i-1][j-d[i].size()]; } } if (f[m][x]) { printf("%d ",m); for (int i=m;i>=1;i--) { if (d[i].size()<=x&&f[i-1][x-d[i].size()]) { x-=d[i].size(); for (int v:d[i]) ans[v]=1; } } if (!ff) for (int i=1;i<=n;i++) if (ans[i]==1) printf("a");else printf("b"); else for (int i=1;i<=n;i++) if (ans[i]==1) printf("b");else printf("a"); } else { printf("%d ",m+1); for (int i=0;i<=m;i++) f[i].clear(); f[0][0]=1; int Max=0,u=-1; for (int i=1;i<=m;i++) if (cnt[i]>Max) { Max=cnt[i]; u=i; } for (int i=1;i<=m;i++) { for (int j=x;j>=0;j--) f[i][j]|=f[i-1][j]; if (i==u) continue; for (int j=x;j>=d[i].size();j--) { f[i][j]|=f[i-1][j-d[i].size()]; } } int ed=-1; for (int i=0;i<x;i++) { if (f[m][i]&&i+Max>=x) { ed=i; break; } } int tt=x-ed; for (int v:d[u]) { if (g[v].size()>1) continue; if (!tt) break; tt--; ans[v]=1; } for (int i=m;i>=1;i--) { if (i==u) continue; if (d[i].size()<=ed&&f[i-1][ed-d[i].size()]) { ed-=d[i].size(); for (int v:d[i]) ans[v]=1; } } if (!ff) for (int i=1;i<=n;i++) if (ans[i]==1) printf("a");else printf("b"); else for (int i=1;i<=n;i++) if (ans[i]==1) printf("b");else printf("a"); } }
看了别人的题解才明白,这里需要套一个Bitset优化背包求解的过程。对比原来的代码理解一下吧。。。
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+100; const int inf=1e9; int n,m,x; int dep[maxn];//节点在第几层 int num[maxn];//每一层的节点个数 int lev[maxn];//每一层的叶子节点个数 bitset<maxn> f[2500]; int w[maxn]; int ok[maxn]; vector<int> g[maxn],v[maxn]; unordered_map<int,int> vis; void dfs1 (int x,int pre) { dep[x]=dep[pre]+1; num[dep[x]]++; m=max(m,dep[x]); if (g[x].size()==1) lev[dep[x]]++; for (int y:g[x]) { if (y==pre) continue; dfs1(y,x); } } void dfs2 (int x,int y) { //找dp状态 if (x==0) return; for (int i=0;i<v[x].size();i++) { if (w[x]>y||f[x-1][y]) break; y-=w[x]; ok[v[x][i]]=1; } dfs2(x-1,y); } int main () { scanf("%d%d",&n,&x); for (int i=2;i<=n;i++) { int y; scanf("%d",&y); g[i].push_back(y); g[y].push_back(i); } dfs1(1,0); int cnt=0; for (int i=1;i<=m;i++) { //第一步,把相同点数的层合并 if (vis[num[i]]) { v[vis[num[i]]].push_back(i); } else { vis[num[i]]=++cnt; w[cnt]=num[i]; v[cnt].push_back(i); } } f[0][0]=1; for (int i=1;i<=cnt;i++) { f[i]=f[i-1]; int sz=v[i].size(); for (int j=1;j<=sz;j<<=1) { sz-=j; f[i]|=(f[i]<<(j*w[i])); } if (sz) f[i]|=f[i]<<(sz*w[i]);//这一步还没懂 } if (f[cnt][x]) { printf("%d ",m); dfs2(cnt,x); for (int i=1;i<=n;i++) { if (ok[dep[i]]) printf("a"); else printf("b"); } } else { int ans=inf; for (int i=x;i>=0;i--) if (f[cnt][i]) { ans=i; break; } dfs2(cnt,ans); int pp=-1; for (int i=1;i<=m;i++) { if (!ok[i]&&lev[i]>=x-ans) { pp=i; break; } } printf("%d ",m+1); for (int i=1;i<=n;i++) { if (dep[i]==pp&&g[i].size()==1) { if (ans==x) printf("b"); else printf("a"),ans++; } else { if (ok[dep[i]]) printf("a"); else printf("b"); } } } }