码迷,mamicode.com
首页 > 移动开发 > 详细

hdoj 2242 考研路茫茫——空调教室 【无向图求边双联通 缩点 + 树形dp】

时间:2015-08-14 01:05:23      阅读:293      评论:0      收藏:0      [点我收藏+]

标签:

考研路茫茫——空调教室

Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 2447    Accepted Submission(s): 721


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

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

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

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

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

Input
本题目包含多组数据,请处理到文件结束。
每组测试第一行包含两个整数N和M(0<N<=10000,0<M<20000)。其中N表示教室的数目(教室编号从0到N-1),M表示通气管道的数目。
第二行有N个整数Vi(0<=Vi<=1000),分别代表每个教室的人数。
接下来有M行,每行两个整数Ai,Bi(0<=Ai,Bi<N),表示教室Ai和教室Bi之间建了一个通气管道。
 

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

Sample Input
4 3 1 1 1 1 0 1 1 2 2 3 4 3 1 2 3 5 0 1 1 2 2 3
 

Sample Output
0 1
 
无语了,无向图边双连通缩点好久没写了。这次写缩点时 由于没把重边过滤掉导致测试数据都过不了,找了好长时间才找到错误。


思路
一:求出总人数total,再用tarjan求出边—双连通EBC,然后缩点构建新图并求出每个EBC里面的人数,把每个EBC里面的人数 看做 其缩点后对应节点的权值。
二:把新图当做一棵树,可以利用树形DP的思想。遍历整棵树,并用num[ u ]记录以u为根的树的 所有节点的点权之和, 每次更新ans = min(ans, abs(total - 2 * num[u])),最后的ans就是答案。



AC代码:

#include <cstdio>
#include <cstring>
#include <stack>
#include <queue>
#include <vector>
#include <cmath>
#include <cstdlib>
#include <algorithm>
#define MAXN 10000+10
#define MAXM 40000+100
#define INF 10000000
using namespace std;
struct Edge
{
    int from, to, next;
};
Edge edge[MAXM];
int head[MAXN], edgenum;
int man[MAXN];
int low[MAXN], dfn[MAXN];
int dfs_clock;
int ebcno[MAXN], ebc_cnt;
stack<int> S;
bool Instack[MAXN];
vector<int> ebc[MAXN];
int N, M;//N个教室 M个管道
int total;//记录所有教室的总人数
void init()
{
    edgenum = 0;
    memset(head, -1, sizeof(head));
}
void addEdge(int u, int v)
{
    Edge E = {u, v, head[u]};
    edge[edgenum] = E;
    head[u] = edgenum++;
}
void getMap()
{
    int a, b;
    while(M--)
    {
        scanf("%d%d", &a, &b);
        addEdge(a, b);
        addEdge(b, a);
    }
}
void tarjan(int u, int fa)
{
    int v, flag = 0;
    low[u] = dfn[u] = ++dfs_clock;
    S.push(u); Instack[u] = true;
    for(int i = head[u]; i != -1; i = edge[i].next)
    {
        v = edge[i].to;
        if(v == fa && !flag)
        {
            flag = 1;
            continue;
        }
        if(!dfn[v])
        {
            tarjan(v, u);
            low[u] = min(low[u], low[v]);
        }
        else if(Instack[v])
            low[u] = min(low[u], dfn[v]);
    }
    if(low[u] == dfn[u])
    {
        ebc_cnt++;
        ebc[ebc_cnt].clear();
        for(;;)
        {
            v = S.top(); S.pop();
            Instack[v] = false;
            ebcno[v] = ebc_cnt;
            ebc[ebc_cnt].push_back(v);
            if(v == u) break;
        }
    }
}
void find_cut(int l, int r)
{
    dfs_clock = ebc_cnt = 0;
    memset(low, 0, sizeof(low));
    memset(dfn, 0, sizeof(dfn));
    memset(ebcno, 0, sizeof(ebcno));
    memset(Instack, false, sizeof(Instack));
    for(int i = l; i <= r; i++)
        if(!dfn[i]) tarjan(i, -1);
}
int ebc_man[MAXN];//记录每个EBC的人数
vector<int> G[MAXN];
void suodian()
{
    for(int i = 1; i <= ebc_cnt; i++)
    {
        G[i].clear(); int sum = 0;
        for(int j = 0; j < ebc[i].size(); j++)
            sum += man[ebc[i][j]];
        ebc_man[i] = sum;
    }
    for(int i = 0; i < edgenum; i+=2)//建图 一开始没加 i+=2 无语。。。
    {
        int u = ebcno[edge[i].from];
        int v = ebcno[edge[i].to];
        if(u != v)
            G[u].push_back(v), G[v].push_back(u);
    }
}
int ans;//最小差值
int num[MAXN];//统计子节点 权值之和
void DFS(int u, int fa)//求解 最小差值
{
     num[u] = ebc_man[u];
     for(int i = 0; i < G[u].size(); i++)
     {
         int v = G[u][i];
         if(v == fa) continue;
         DFS(v, u);
         num[u] += num[v];
     }
     ans = min(ans, abs(total - 2 * num[u]));//更新
}
void solve()
{
    find_cut(0, N-1);//找EBC
    if(ebc_cnt == 1)//只有一个EBC
    {
        printf("impossible\n");
        return ;
    }
    suodian();//缩点
    ans = INF;
    DFS(1, -1);//遍历整棵树
    printf("%d\n", ans);
}
int main()
{
    while(scanf("%d%d", &N, &M) != EOF)
    {
        total = 0;
        for(int i = 0; i < N; i++)
            scanf("%d", &man[i]), total += man[i];//统计总人数
        init();
        getMap();
        solve();
    }
    return 0;
}


版权声明:本文为博主原创文章,未经博主允许不得转载。

hdoj 2242 考研路茫茫——空调教室 【无向图求边双联通 缩点 + 树形dp】

标签:

原文地址:http://blog.csdn.net/chenzhenyu123456/article/details/47629337

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!