2020牛客暑期多校训练营(第二场)C.Cover the Tree(树性转线性,dfs序)

地址:https://ac.nowcoder.com/acm/contest/5667/C

题意:

给定一个k个节点的无根树,求最少的链覆盖树上的所有边。并输出覆盖方式(链的两边的节点编号)

解析:

保证每一个边都有被覆盖,肯定与叶子节点有关系。

有结论:

用最少条链来覆盖一棵树的时候,最优解: (叶子结点数+1) / 2

对于叶子结点的求法,可以用vector建图,求叶子结点的dfs序:

dfs(i,-1);
int dfs(int now,int fa)//fa防止走回头路!
{
    if(v[now].size()==1)
    {
        a[tot++]=now;
        return 0;
    }
    for(int i=0;i<v[now].size();i++)
    {
        if(v[now][i]!=fa)
        {
            dfs(v[now][i],now);
        }
    }
}

网上找的专业分析:

 叶子结点:i与(i+sum/2)连接即可。

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn=2e5+20;
vector<int>v[maxn];
int d[maxn],a[maxn];
int tot=1;
int sum=0;
int dfs(int now,int fa)
{
    if(v[now].size()==1)
    {
        a[tot++]=now;
        return 0;
    }
    for(int i=0;i<v[now].size();i++)
    {
        if(v[now][i]!=fa)
        {
            dfs(v[now][i],now);
        }
    }
}
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<n;i++)
    {
        int a,b;
        cin>>a>>b;
        v[a].push_back(b);
        v[b].push_back(a);
    }
    for(int i=1;i<=n;i++)
    {
        if(v[i].size()>1)
        {
            dfs(i,-1);
            break;
        }
    }
    tot--;
    cout<<(tot+1)/2<<endl;
    for(int i=1;i<=(tot+1)/2;i++)
        cout<<a[i]<<" "<<a[i+tot/2]<<endl;
}

不用dfs序的简单代码:

#include<bits/stdc++.h>
typedef long long ll;
using namespace std;
const int maxn=2e5+20;
int d[maxn],a[maxn];
int main()
{
    int n;
    cin>>n;
    for(int i=1;i<n;i++)
    {
        int a,b;
        cin>>a>>b;
        d[a]++;
        d[b]++;
    }
    int sum=0;
    int tot=1;
    for(int i=1;i<=n;i++)
    {
        if(d[i]==1)
            a[tot++]=i,sum++;
    }
    cout<<(sum+1)/2<<endl;
    for(int i=1;i<=(sum+1)/2;i++)
        cout<<a[i]<<" "<<a[i+(sum)/2]<<endl;   
}
原文地址:https://www.cnblogs.com/liyexin/p/13326261.html