F. Removing Leaves
题意有一棵树,共有n个节点 n-1条边
每次从同一个节点上去除k个叶子,问最多去除几次。
题解
贪心的策略是:每次剪掉即可。因为剪去叶子不会对其他的节点产生负影响。也就是
说,剪除一次以后,只会说多出一个节点可以减,不会说剪除了以后导致其他节点减不
了。因此这道题根本不用考虑剪枝的先后问题,直接剪就行了。
具体实现
考虑类比拓扑排序,将叶子结点入队,然后对其相邻结点遍历,当被遍历次数到达k
次,ans++,如果此时度数为1就入队,注意需要用vis数组标记一下那些结点已经入过
队,且标记时需要在queue,一次标记一个,因为存在edge(u,v),这样的情况,度数都
是1,但是如果都标记了,就算不了贡献了
参考博客 参考博客
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48
| #include <bits/stdc++.h> using namespace std; typedef long long ll; #define endl '\n' #define Turnoff std::ios::sync_with_stdio(false) const ll Max=2e5+5; vector<int>mp[Max]; int edge[Max],viscnt[Max]; bool vis[Max]; int main() { Turnoff; int t;cin>>t; while(t--){ int n,k;cin>>n>>k; for(int i=1;i<=n;i++)edge[i]=0,mp[i].clear(); for(int i=1;i<n;i++){ int u,v;cin>>u>>v; mp[u].push_back(v); mp[v].push_back(u); edge[u]++;edge[v]++; } if(k==1)cout<<n-1<<endl; else{ queue<int>leave;int ans=0; for(int i=1;i<=n;i++){ if(edge[i]==1)leave.push(i); vis[i]=0,viscnt[i]=0; } while(!leave.empty()){ int u=leave.front(); leave.pop();vis[u]=1; for(auto i:mp[u]){ if(vis[i])continue; viscnt[i]++;edge[i]--; if(viscnt[i]%k==0){ ans++; if(edge[i]==1)leave.push(i); } } } cout<<ans<<endl; } } }
|