空调教室 【无向图求边双联通 缩点 + 树形dp】

考研路茫茫——空调教室

Time Limit: 4000/2000 MS (Java/Others)Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 2447Accepted 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 31 1 1 10 11 22 34 31 2 3 50 11 22 3

Sample Output

01

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

思路

一:求出总人数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 10000000using 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);//找EBCif(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;}

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

最快乐的时候,就是去旅行。

空调教室 【无向图求边双联通 缩点 + 树形dp】

相关文章:

你感兴趣的文章:

标签云: