UVa1670/LA5920 Kingdom Roadmap

UVa1670/LA5920 Kingdom Roadmap

题目链接

  本题是2011年icpc欧洲区域赛东北欧赛区K

题意

  输入一个n(n≤100000)个结点的树,添加尽量少的边,使得任意删除一条边之后图仍然连通。如下图所示,最优方案用虚线表示。
Kingdom Roadmap

分析

  首先统计叶结点数c,即可知道答案是 ⌈ c 2 ⌉ \lceil\frac{c}2\rceil 2c,然后将叶结点两两配对连边即可,不过注意要优先将不在同一个枝杈的两叶结点配对(看下图就能理解)。
Kingdom Roadmap
  可以遍历每个叶结点找出它连接到的枝杈点,这样就统计出了每个枝杈点有哪些叶结点,然后对每一个枝杈点先拿出一个叶结点做配对,其他叶结点存着等后面再任意配对即可。
  还可以任取一个非叶结点作为根对树做dfs遍历,遍历过程遇到叶结点就存起来,将存起来的叶结点看成循环队列则可以发现属于同一个枝杈点的叶结点处在队列连续的一段。这时候就很容易对叶结点做配对了:将第 i i i个叶结点和第 i + ⌊ c 2 ⌋ i+\lfloor\frac{c}2\rfloor i+2c个叶结点配对连边。

AC 代码

  本题UVa数据出问题了,提交必WA,Codeforces上对应的题目是GYM 100085K,可提交(先加上freopen,输入自 kingdom.in ,输出到 kingdom.out)。

  按枝杈点点对叶结点做分类的方法

#include <iostream>
#include <vector>
using namespace std;

#define N 100100
int q[N], n; vector<int> g[N], gg[N];

void solve() {
    int c = 0;
    for (int i=1; i<=n; ++i) g[i].clear(), gg[i].clear();
    for (int i=1, u, v; i<n; ++i) cin >> u >> v, g[u].push_back(v), g[v].push_back(u);
    for (int i=1; i<=n; ++i) if (g[i].size() == 1) {
        int u = g[i][0], p = i, v; ++c;
        while (g[u].size() == 2) v = u, u = g[u][0]+g[u][1]-p, p = v;
        gg[u].push_back(i);
    }
    cout << (c+1)/2 << endl;
    int head = 0, tail = 0;
    for (int i=1; i<=n; ++i) {
        int s = gg[i].size();
        if (s>1 && head<tail) cout << q[head++] << ' ' << gg[i].back() << endl, --s;
        for (int j=0; j<s; ++j) q[tail++] = gg[i][j];
    }
    for (int i=head+1; i<tail; i+=2) cout << q[i-1] << ' ' << q[i] << endl;
    if (c&1) cout << q[0] << ' ' << q[tail-1] << endl;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    while (cin >> n) solve();
    return 0;
}

  dfs法

#include <iostream>
#include <vector>
using namespace std;

#define N 100100
int q[N], n, c; vector<int> g[N];

void dfs(int u, int p = -1) {
    if (g[u].size() == 1) q[c++] = u;
    for (int i=g[u].size()-1, v; i>=0; --i) if ((v = g[u][i]) != p) dfs(v, u);
}

void solve() {
    c = 0;
    for (int i=1; i<=n; ++i) g[i].clear();
    for (int i=1, u, v; i<n; ++i) cin >> u >> v, g[u].push_back(v), g[v].push_back(u);
    for (int i=1; i<=n; ++i) if (g[i].size() > 1) {
        dfs(i); break;
    }
    cout << (c+1)/2 << endl;
    for (int i=0, m=c>>1; i<m; ++i) cout << q[i] << ' ' << q[i+m] << endl;
    if (c&1) cout << q[0] << ' ' << q[c-1] << endl;
}

int main() {
    ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
    while (cin >> n) solve();
    return 0;
}
  • 10
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值