[SPOJ UOFTCG Office Mates]

[SPOJ UOFTCG Office Mates]

分类:dfs tree

1. 题目链接

[SPOJ UOFTCG - Office Mates]

2. 题意描述

题意:
有N个学生,M 对朋友关系,学生只能挨着他的朋友坐。
桌子排列成一条直线,可以让一些桌子空出来,数据保证对于任何含K个学生的集合,最多只有K-1对朋友。
求最少需要多少张桌子。
数据:
(N<=100000)
输入第一行表示数据组数 T
对于每组测试数据,第一行为 N 和 M ,接下来 M 行表示 M 对朋友关系
输入文件不会超过2M

3. 解题思路

题意简单的描述就是最少链覆盖一个森林所有的顶点,求最少链数。(所有顶点只能使用一次)。那么显然答案就是顶点数+最少链数-1.
怎么求最少链数呢?
对于森林中的每一棵树,找到一个深度最大且儿子树目 2 的节点。将以这个节点为根节点的子树进行拆解。(这样就保证了这个节点是由若干条链组成的,而不是由子树)。
如果还不懂? 在纸上模拟一遍过程就很清楚啦。

4. 实现代码

#include <bits/stdc++.h>
using namespace std;

typedef long long LL;
typedef long double LB;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;

const int INF = 0x3f3f3f3f;
const LL INFL = 0x3f3f3f3f3f3f3f3fLL;
const LB eps = 1e-8;
const int MAXN = 100000 + 7;

template <typename T>
inline bool scan_d (T &ret) {
    char c;
    int sgn;
    if (c = getchar(), c == EOF) return 0; //EOF
    while (c != '-' && (c < '0' || c > '9') ) c = getchar();
    sgn = (c == '-') ? -1 : 1;
    ret = (c == '-') ? 0 : (c - '0');
    while (c = getchar(), c >= '0' && c <= '9') ret = ret * 10 + (c - '0');
    ret *= sgn;
    return 1;
}
template<typename T>
void print(T x) {
    static char s[33], *s1; s1 = s;
    if (!x) *s1++ = '0';
    if (x < 0) putchar('-'), x = -x;
    while(x) *s1++ = (x % 10 + '0'), x /= 10;
    while(s1-- != s) putchar(*s1);
}
template<typename T> void println(T x) { print(x); putchar('\n');}

int T, n, m;

struct Edge {
    int v, next;
} edge[MAXN << 1];
int head[MAXN], tot, fa[MAXN], du[MAXN], link[MAXN];
bool used[MAXN];
struct QNode {
    int u, dep;
    bool operator < (const QNode& e) const { return dep < e.dep; }
};
queue<QNode> Q;

void init() {
    tot = 0;
    memset(head, -1, sizeof(head));
    memset(du, -1, sizeof(du));
    memset(used, false, sizeof(used));
}
inline void add_edge(int u, int v) {
    edge[tot] = Edge{v, head[u]};
    head[u] = tot ++;
}

void dfs(int u, int pre, int k) {
    fa[u] = pre;
    du[u] = 0;
    for(int i = head[u], v; ~i; i = edge[i].next) {
        v = edge[i].v;
        if(v == pre) continue;
        ++ du[u];
        dfs(v, u, k + 1);
    }
    if(du[u] >= 2) Q.push(QNode{u, k});
}

void dfs2(int u) {
    if(used[u]) return;
    used[u] = true;
    for(int i = head[u], v; ~i; i = edge[i].next) {
        v = edge[i].v;
        if(fa[u] == v) continue;
        dfs2(v);
    }
    head[u] = -1;
}

int main() {
#ifdef ___LOCAL_WONZY___
    freopen("input.txt", "r", stdin);
#endif // ___LOCAL_WONZY___
    int u, v;
    scan_d(T);
    while(T --) {
        init();
        scan_d(n), scan_d(m);
        for(int i = 1; i <= m; ++i) {
            scan_d(u), scan_d(v);
            add_edge(u, v), add_edge(v, u);
        }
        for(int i = 1; i <= n; ++i) {
            if(~du[i]) continue;
            dfs(i, -1, 1);
        }
        int cnt = 0;
        while(!Q.empty()) {
            u = Q.front().u; Q.pop();
            if(used[u] || du[u] < 2) continue;
            cnt += du[u] - 1;
            if(~fa[u]) -- du[fa[u]];
            dfs2(u);
        }
        set<int> s;
        for(int i = 1; i <= n; ++i) {
            if(!used[i] && du[i] == 0) {
                u = i;
                while(~fa[u] && !used[fa[u]]) u = fa[u], used[u] = true;
                s.insert(u);
            }
        }
        cnt += s.size();
        println(n + cnt - 1);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值