hdoj 2242 考研道茫茫——空调教室 【无向图求边双联通 缩点 + 树形dp】
hdoj 2242 考研路茫茫——空调教室 【无向图求边双联通 缩点 + 树形dp】
考研路茫茫——空调教室
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条通气管道,让整个教学楼的全部教室都直接或间接和空调教室连通上,构成了教室群,于是,全部教室都能吹到空调了。
不仅仅这样,学校发现教室人数越来越多,单单一个空调已经不能满足大家的需求。于是,学校决定封闭掉一条通气管道,把全部教室分成两个连通的教室群,再在那个没有空调的教室群里添置一个空调。
当然,为了让效果更好,学校想让这两个教室群里的学生人数尽量平衡。于是学校找到了你,问你封闭哪条通气管道,使得两个教室群的人数尽量平衡,并且输出人数差值的绝对值。
一个炎热的下午,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之间建了一个通气管道。
每组测试第一行包含两个整数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"。
如果不管封闭哪条管道都不能把教室分成两个教室群,就输出"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; }
版权声明:本文为博主原创文章,未经博主允许不得转载。