HDU - 2242 考研路茫茫――空调教室(树形DP+强连通分量)

题目大意:众所周知,HDU的考研教室是没有空调的,于是就苦了不少不去图书馆的考研仔们。Lele也是其中一个。而某教室旁边又摆着两个未装上的空调,更是引起人们无限YY。

一个炎热的下午,Lele照例在教室睡觉的时候,竟然做起了空调教室的美梦。

Lele梦到学校某天终于大发慈悲给某个教室安上了一个空调。而且建造了了M条通气管道,让整个教学楼的全部教室都直接或间接和空调教室连通上,构成了教室群,于是,全部教室都能吹到空调了。

不仅仅这样,学校发现教室人数越来越多,单单一个空调已经不能满足大家的需求。于是,学校决定封闭掉一条通气管道,把全部教室分成两个连通的教室群,再在那个没有空调的教室群里添置一个空调。

当然,为了让效果更好,学校想让这两个教室群里的学生人数尽量平衡。于是学校找到了你,问你封闭哪条通气管道,使得两个教室群的人数尽量平衡,并且输出人数差值的绝对值。

解题思路:找出所有的边-双连通分量,这些边-双连通分量是无法进行操作的,因为删掉边并不能增加连通分量
接着将所有的边-双连通分量缩点,用桥进行连接,这样就形成了一棵无根树了
特判,只有一个边-双连通分量的时候
接着在这棵树上进行DP即可
这题要注意的是有可能会重边,所以要注意

#include <cstdio>
#include <cstring>

#define min(a, b) ((a) < (b) ? (a) : (b))
#define abs(x) ((x) > 0? (x): (-(x)))
#define N 10010
#define M 40010

struct Edge {
    int to, next;
}E[M];

struct Node{
    int x, y;   
}node[M];

int cost[N], head[N], bccno[N], pre[N], stack[N], low[N], num[N];
int Sum, n, m, tot, dfs_clock, bcc_cnt, top;
bool vis[N];

void AddEdge(int u, int v) {
    E[tot].to = v; E[tot].next = head[u]; head[u] = tot++;
    u = u ^ v; v = u ^ v; u = u ^ v;
    E[tot].to = v; E[tot].next = head[u]; head[u] = tot++;
}

void init() {
    Sum = 0;

    for (int i = 0; i < n; i++) {
        scanf("%d", &cost[i]);
        Sum += cost[i];
    }

    memset(head, -1, sizeof(head));
    tot = 0;

    int u, v;
    for (int i = 0; i < m; i++) {
        scanf("%d%d", &u, &v);
        node[i].x = u; node[i].y = v;
        AddEdge(u, v);
    }
}

void dfs(int u, int fa) {
    low[u] = pre[u] = ++dfs_clock;
    vis[u] = 1;
    stack[++top] = u;
    bool flag =false;
    for (int i = head[u]; i != -1; i = E[i].next) {
        int v = E[i].to;
        if (v == fa && !flag) {flag = 1; continue;}//两条边的话,只封住一条是没有用的
        if (!vis[v]) dfs(v, u);
        low[u] = min(low[u], low[v]);
    }

    int x;
    if (pre[u] == low[u]) {
        bcc_cnt++;
        num[bcc_cnt] = 0;
        while (1) {
            x = stack[top--];
            bccno[x] = bcc_cnt;
            num[bcc_cnt] += cost[x];
            if (x == u)
                break;
        }
    }
}

int Min;
int DP(int u, int fa) {
    int All = num[u];
    for (int i = head[u]; i != -1; i = E[i].next) {
        int v = E[i].to;
        if (v != fa)
            All += DP(v, u);
    }
    Min = min(Min, abs(Sum - 2 * All));
    return All;
}

void solve() {
    memset(bccno, 0, sizeof(bccno));
    memset(pre, 0, sizeof(pre));
    memset(vis, 0, sizeof(vis));
    dfs_clock = bcc_cnt = top = 0;
    dfs(0, -1);
    if (bcc_cnt == 1) {
        printf("impossible\n");
        return ;
    }
    memset(head, -1, sizeof(head));
    tot = 0;

    int x, y;
    for (int i = 0; i < m; i++) {
        x = node[i].x;
        y = node[i].y;

        if (bccno[x] != bccno[y])
            AddEdge(bccno[x], bccno[y]);
    }
    Min = 0x3f3f3f3f;
    DP(1, -1);
    printf("%d\n", Min);
}

int main() {
    while (scanf("%d%d", &n, &m) != EOF) {
        init();
        solve();
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值