[刷题]HDU2242

来源:互联网 发布:java auth2.0开源框架 编辑:程序博客网 时间:2024/05/17 02:16

Problem Description

众所周知,HDU的考研教室是没有空调的,于是就苦了不少不去图书馆的考研仔们。Lele也是其中一个。而某教室旁边又摆着两个未装上的空调,更是引起人们无限YY。
一个炎热的下午,Lele照例在教室睡觉的时候,竟然做起了空调教室的美梦。
Lele梦到学校某天终于大发慈悲给某个教室安上了一个空调。而且建造了了M条通气管道,让整个教学楼的全部教室都直接或间接和空调教室连通上,构成了教室群,于是,全部教室都能吹到空调了。
不仅仅这样,学校发现教室人数越来越多,单单一个空调已经不能满足大家的需求。于是,学校决定封闭掉一条通气管道,把全部教室分成两个连通的教室群,再在那个没有空调的教室群里添置一个空调。
当然,为了让效果更好,学校想让这两个教室群里的学生人数尽量平衡。于是学校找到了你,问你封闭哪条通气管道,使得两个教室群的人数尽量平衡,并且输出人数差值的绝对值。

Input

本题目包含多组数据,请处理到文件结束。
每组测试第一行包含两个整数N和M(0

Output

对于每组数据,请在一行里面输出所求的差值。
如果不管封闭哪条管道都不能把教室分成两个教室群,就输出”impossible”。

Sample Input

4 31 1 1 10 11 22 34 31 2 3 50 11 22 3

Sample Output

01

Key1

边的双连通分量。首先是求分量,然后缩点。然后简单的树形DP即可。

要点是,它的图可以有重边。被坑了好久。

Code1

#include<algorithm>#include<iostream>#include<cstring>#include<vector>#include<cmath>#include<set>const int Maxn = 10000 + 23;using namespace std;class CutEdge {private:    int N;    int clk, pre[Maxn];    int DFS(int u, int f) {        int lowu = pre[u] = ++clk;        for (auto e = G[u].begin(); e != G[u].end(); ++e) {            int v = *e;            if (!pre[v]) {                int lowv = DFS(v, u);                lowu = min(lowu, lowv);                if (lowv > pre[u]) {                    Cut[u].insert(v);                    Cut[v].insert(u);                }            }            else if (pre[u] > pre[v]) {                int cnt = 0; //重复边的处理                for (const auto &e : G[u]) if (e == v) ++cnt;                if (cnt > 1 || v != f) {                    lowu = min(lowu, pre[v]);                }            }        }        return lowu;    }    //求边双联通部分    void DFS2(int u, int id) {        ID[u] = id;        for (const auto &v : G[u]) if (!ID[v]) {            if (Cut[u].count(v)) {                ++Num;                G2[id].push_back(Num);                G2[Num].push_back(id);                DFS2(v, Num);            }            else DFS2(v, id);        }    }public:    vector<int> G[Maxn];    set<int> Cut[Maxn];    //求边双联通部分    vector<int> G2[Maxn]; //缩点后的图(以ID为结点)    int ID[Maxn]; //每个点所在的子图    int Num; //ID个数    void Clear(int n) {        N = n;        memset(ID, 0, sizeof(ID));        memset(pre, 0, sizeof(pre));        for (int i = 1; i <= N; ++i) {            G[i].clear();            G2[i].clear();            Cut[i].clear();        }        clk = 0;        Num = 1;    }    void AddEdge(int u, int v) {        G[u].push_back(v);        G[v].push_back(u);    }    void Find() {        for (int i = 1; i <= N; ++i)            if (!pre[i])                DFS(i, -1);    }    //求边双联通部分    int BCC() {        DFS2(1, Num);        return Num;    }};int N, M;int V[Maxn];int V2[Maxn];int DP[Maxn];bool vis[Maxn];CutEdge C;int DFS(int u) {    vis[u] = true;    DP[u] = V2[u];    for (const auto &v : C.G2[u]) {        if (!vis[v]) DP[u] += DFS(v);    }    return DP[u];}int main() {    ios::sync_with_stdio(false);    while (cin >> N >> M) {        memset(V2, 0, sizeof(V2));        memset(vis, 0, sizeof(vis));        memset(DP, 0, sizeof(DP));        C.Clear(N);        for (int i = 1; i <= N; ++i) cin >> V[i];        int u, v;        for (int i = 1; i <= M; ++i) {            cin >> u >> v;            C.AddEdge(++u, ++v);        }        C.Find();        C.BCC();        if (C.Num == 1) {            cout << "impossible\n";            continue;        }        int sum = 0;        for (int i = 1; i <= N; ++i) {            sum += V[i];            V2[C.ID[i]] += V[i];        }        DFS(1);        int res = sum;        for (int i = 1; i <= C.Num; ++i) {            res = min(abs(sum - 2 * DP[i]), res);        }        cout << res << '\n';    }    return 0;}

Key2

可以的优化是,其实在第一个DFS的时候,由于知道了桥的位置,可以在DFS的时候直接求出v以及v的子节点的和。那么在找到桥{u, v}的时候就直接顺便DP了。

Code2

#include<algorithm>#include<iostream>#include<cstring>#include<vector>#include<cmath>#include<set>const int Maxn = 10000+23;using namespace std;int N, M;int V[Maxn];int res,sum;class BCC {private:    int N;    int clk, pre[Maxn];    int DFS(int u, int f) {        int &lowu = ID[u];        lowu= pre[u] = ++clk;        for (auto e = G[u].begin(); e != G[u].end(); ++e) {            int v = *e;            if (!pre[v]) {                int lowv = DFS(v, u);                lowu = min(lowu, lowv);                if (lowv > pre[u]) {                    ++Num;                    res = min(abs(sum - 2 * V[v]), res);                }            }            else if (pre[u] > pre[v]) {                int cnt = 0; //重复边的处理                for (const auto &e : G[u]) if (e == v) ++cnt;                if (cnt > 1 || v != f) {                    lowu = min(lowu, pre[v]);                }            }            /*else if (pre[u]>pre[v] && v != f)                lowu = min(lowu, pre[v]);*/        }        if(f!=-1) V[f] += V[u];        return lowu;    }public:    vector<int> G[Maxn];    int ID[Maxn];    int Num;    void Clear(int n) {        N = n;        memset(ID, 0, sizeof(ID));        memset(pre, 0, sizeof(pre));        for (int i = 1; i <= N; ++i) {            G[i].clear();        }        clk = 0;        Num = 1;    }    void AddEdge(int u, int v) {        G[u].push_back(v);        G[v].push_back(u);    }    int Find() {        for (int i = 1; i <= N; ++i)            if (!pre[i])                DFS(i, -1);        return Num;    }};BCC C;int main() {    ios::sync_with_stdio(false);    while (cin >> N >> M) {        C.Clear(N);        sum = 0;        for (int i = 1; i <= N; ++i) {            cin >> V[i];            sum += V[i];        }        int u, v;        for (int i = 1; i <= M; ++i) {            cin >> u >> v;            C.AddEdge(++u, ++v);        }        res = sum;        if (C.Find() == 1) {            cout << "impossible\n";        }        else cout << res << '\n';    }    return 0;}
原创粉丝点击