codeforce 1041 E. Tree Reconstruction 思维题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/lifelikes/article/details/83017971
E. Tree Reconstruction
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

Monocarp has drawn a tree (an undirected connected acyclic graph) and then has given each vertex an index. All indices are distinct numbers from 11) — vertices connected by an edge.

Note: The numeration of edges doesn't matter for this task. Your solution will be considered correct if your tree produces the same pairs as given in the input file (possibly reordered). That means that you can print the edges of the tree you reconstructed in any order.

Examples
Input
Copy
4
3 4
1 4
3 4
Output
Copy
YES
1 3
3 2
2 4
Input
Copy
3
1 3
1 3
Output
Copy
NO
Input
Copy
3
1 2
2 3
Output
Copy
NO
Note

Possible tree from the first example. Dotted lines show edges you need to remove to get appropriate pairs.

题意: 给出一棵树和一个K 现在要将这棵树的叶子节点分为n个集合,使得每个集合内的任意两个节点的距离不大于k

解题思路:
首先,要很容易想到,大致算法是用dfs 一层一层的合并。
假设S[u] 为以u节点为跟的所有叶子节点的最小分割集合。
假设现在搜到了u节点,我们能知道u节点的所有儿子节点的S[son[u]]
考虑如何是否能贪心的去合并。
假设我们能知道每个叶子节点到u的距离,那我们就可以根据这个给S[son[u]] 排序
从小到大依次判断是否能合并。
想到这里的话,如果用启发式合并 可以做到nlognlogn的复杂度 要用到set,常数较大,感觉过不了。
但再仔细想(看)一(题)下(解) 对于每个子集合最多只有一个集合能合并到其他兄弟节点的集合里。
所以我们只需要记录下S[u] 中 最大叶子节点深度最小 的那个子集合深度就可以了。
具体可以看代码。

#include <bits/stdc++.h>
using namespace std;
class Edge{
public:
    int v,next;
};
const int MAX = 1e6+10;
Edge edge[MAX<<1];
int head[MAX];
int tot;
void init(){
    memset(head,-1,sizeof head);
    tot=0;
}
void add(int u,int v){
    edge[tot].v=v;
    edge[tot].next=head[u];
    head[u]=tot;
    tot++;
}
int ans=0;
int n,k;
int dfs(int u,int pre){
    priority_queue<int> PQ;
    for(int i= head[u];i!=-1;i=edge[i].next){
        int v= edge[i].v;
        if(v==pre) continue;
        int cnt = dfs(v,u)+1;
        PQ.push(-cnt);
    }
    int cnt1=0,cnt2=0;
    if(!PQ.empty()){
        cnt1=-PQ.top();
        PQ.pop();
    }
    while(!PQ.empty()){
        cnt2= -PQ.top();
        PQ.pop();
        if(cnt1+cnt2<=k){
            cnt1=cnt2;
            ans--;
        }else{
            break;
        }
    }
    //cout<<u<<":"<<cnt1<<endl;
    return cnt1;
}
int du[MAX];
int main(){
    int root=1;
    init();
    scanf("%d %d",&n,&k);
    for(int i=1;i<n;i++){
        int u,v;
        scanf("%d %d",&u,&v);
        add(u,v);
        add(v,u);
        du[v]++;
        du[u]++;
        if(du[v]>=2){
            root=v;
        }
        if(du[u]>=2){
            root=u;
        }
    }
    ans=0;
    for(int i=1;i<=n;i++){
        ans+=(du[i]==1);
    }
    dfs(root,-1);
    cout<<ans<<endl;
    return 0;
}

猜你喜欢

转载自blog.csdn.net/lifelikes/article/details/83017971