一个比较奇怪的情况,每次砍掉一颗子树,其他会拓展,
求最小扩展数,
准备想dp,但是仔细看一下这个过程,
在某一阶段,我有dep_sz[nw]个点会继续扩展,其他的被砍了,
然后我挑一个点隔离,其他的还是继续扩展,
这里的哪个点怎么挑呢?
与子树节点和有关?贪心选择son_sz最大的点,会wa,因为还有一个g_sz变量
而且每次隔离这个点后,这个点的son_sz都没了,
但是受影响的是他的兄弟,
并且兄弟的情况会变得很复杂......
有下一层隔断的,下下层隔断的,时间各不相同
但是我们可以从上面的思路中,发现,
我们总共会隔离最多mx_dep次,
并且,第i次,隔离的必定为dep==i+1次(root的深度为1)
每次被隔离的其实不多,更多的是还在感染的,
这样dp其实反而效率不高,搜索更快
所以就以dep为阶段,搜索
注意标记的过程,这样最快,取消也最容易,但是前一种写法是错的
void dfs_0(int rt,int dp) { sz[rt]=1; dep[dp].push_back(rt),dep_sz[dp]++; g_sz[rt]=g[rt].size() ; for(int i=0;i<g_sz[rt];i++) if(g[rt][i]!=fa[rt]) { fa[g[rt][i]]=rt; dfs_0(g[rt][i],dp+1); sz[rt]+=sz[g[rt][i]]; } }
void dfs(int dp,int ans) { int res=dep_sz[dp]; for(int i=0;i<dep_sz[dp];i++) { int rt=dep[dp][i]; if(vis[fa[rt]]) { res--; vis[rt]=true; } else vis[rt]=false; } if(!res) min_ans=min(min_ans,ans); else for(int i=0;i<dep_sz[dp];i++) { int rt=dep[dp][i]; if(!vis[rt]) { vis[rt]=true; dfs(dp+1,ans-sz[rt]); vis[rt]=false; } } }
全部代码:
#include<cstdio> #include<cstdlib> #include<vector> using namespace std; int n,m; const int N=303; vector <int> g[N],dep[N]; int g_sz[N],dep_sz[N]; int sz[N],fa[N]; void dfs_0(int rt,int dp) { sz[rt]=1; dep[dp].push_back(rt),dep_sz[dp]++; g_sz[rt]=g[rt].size() ; for(int i=0;i<g_sz[rt];i++) if(g[rt][i]!=fa[rt]) { fa[g[rt][i]]=rt; dfs_0(g[rt][i],dp+1); sz[rt]+=sz[g[rt][i]]; } } int min_ans; bool vis[N]; void dfs(int dp,int ans) { int res=dep_sz[dp]; for(int i=0;i<dep_sz[dp];i++) { int rt=dep[dp][i]; if(vis[fa[rt]]) { res--; vis[rt]=true; } else vis[rt]=false; } if(!res) min_ans=min(min_ans,ans); else for(int i=0;i<dep_sz[dp];i++) { int rt=dep[dp][i]; if(!vis[rt]) { vis[rt]=true; dfs(dp+1,ans-sz[rt]); vis[rt]=false; } } } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int u,v; scanf("%d%d",&u,&v); g[u].push_back(v),g[v].push_back(u); } dfs_0(1,1); min_ans=n-1; dfs(2,n); for(int i=2;i<=n;i++) if(!sz[i]) min_ans--; printf("%d ",min_ans); return 0; }