POJ
来源:互联网 发布:小天才手表没有网络 编辑:程序博客网 时间:2024/06/06 19:47
POJ - 3177
题意+思路:问最少能补成几条边,使这个图成为边双连通。
具体的做法是把每个双连通分量缩成一个点,(求边双连通分量: 先找桥,然后把所有的桥从图中删去,这样图中剩下的几块连起来的子图,每个子图是一个边双连通分量),缩点的时候,把所有的桥加回来,而这题所求的加最少的边来补全成双连通图,ans = (num(leaf) + 1) / 2 ;
原因:缩点后的图,可以看作一棵树,树上的每条边都是需要补充的桥,那么我们肯定是先找树上路径最长的,再找次长的,直到成为一个双连通。树上路径最长的起点和终点肯定是在叶子结点上,所以每次找lca最远的两个叶子结点,把他们连起来,所有偶数个叶子节点的时候,刚好是num(leaf)/2,奇数就是(num(leaf)+1)/2,所以就有了那个式子。
这题的去重我是直接在add()加边的时候操作的,但是这样其实不够快,毕竟每次都要找以u为起点的边。
参考了一下别人的代码,tarjan中会有一个参数fa来判断是否是走过来的那条边,如果用这条边在edge中的下标来传递参数,那么在tarjan中就可以判断一下到底这条边是走过来的那条边,还是重复的边了,如果是重复的边,还是让它去往下去更新,这样就能得到正确的结果。
#include <cstdio>#include <cstring>#include <iostream>#include <algorithm>#include <map>using namespace std;const int maxe = 1e4 + 10, maxn = 5e3 + 50;struct node{ int to,next; int tag,isb; //判断重边与是否为桥 node(){} node(int a,int b,int c,int d) {to = a; next = b, tag = c, isb = d;}}edge[maxe << 2];int h[maxn], dfn[maxn], low[maxn], bid[maxn] , vis[maxn];int in[maxn], isb[maxn];int n,m;int edgenum,tot;void init(){ for(int i = 1; i <= n; i++) h[i] = -1, low[i] = dfn[i] = bid[i] = vis[i] = 0; for(int i = 1; i <= n ; i++) bid[i] = in[i] = isb[i] = 0; edgenum = tot = 0;}void add(int u,int to){ int flag = -1; //判重 for(int i = h[u]; ~i; i = edge[i].next) { int v = edge[i].to; if(v == to) {flag = i;break;} } if(~flag) {edge[flag].tag = 1; return;} edge[edgenum] = node(to,h[u],0,0); h[u] = edgenum++;}int tarjan(int u,int pre) //找桥标记一下{ dfn[u] = low[u] = ++tot; for(int i = h[u]; ~i ; i = edge[i].next) { int v = edge[i].to; if(!dfn[v]) { tarjan(v,u); low[u] = min(low[u],low[v]); if(!edge[i].tag && low[v] > dfn[u]) { edge[i].isb = 1; edge[i^1].isb = 1; } } else if(dfn[v] < dfn[u] && v != pre) low[u] = min(low[u],dfn[v]); } return 0;}void shrink(int u,int root,int pre){ bid[u] = root; vis[u] = true; for(int i = h[u]; ~i; i = edge[i].next) { int v = edge[i].to; if(!edge[i].isb && v != pre && !vis[v]) shrink(v,root,u); }}int solve(){ for(int i = 1; i <= n ; i++) for(int j = h[i]; ~j ; j = edge[j].next) { int u = i, v = edge[j].to; if(edge[j].isb == 0) continue; //如果不是桥 in[bid[u]] ++; } int ans = 0; //for(int i = 1;i <= n ; i++) // cout << isb[i] << " " << in[i] << endl; for(int i = 1; i <= n ; i++) if(isb[i] && in[i] == 1) ans++; printf("%d\n",(ans+1)/2); return 0;}int main(){ //freopen("D://in.txt","r",stdin); while(~scanf("%d%d",&n,&m)) { init(); for(int i = 0; i < m ; i++) { int a,b; scanf("%d%d",&a,&b); add(a,b); add(b,a); } //找桥标记桥 for(int i = 1 ; i <= n ; i++) if(!dfn[i]) tarjan(i,-1); /*for(int i = 1; i <= n; i++) for(int j = h[i]; ~j; j = edge[j].next) { int v = edge[j].to; cout <<"f to isb "<< i << " " << v << " " << edge[j].isb << endl; } */ for(int i = 1 ; i <= n ; i++) if(!bid[i]) { isb[i] = true; shrink(i,i,-1); } solve(); } return 0;}
阅读全文
0 0
- POJ
- poj
- POJ
- POJ
- poj
- poj
- POJ
- POJ
- poj
- POJ
- POJ
- POJ
- POJ
- POJ
- POJ
- POJ
- POJ
- POJ
- 如何停止Handler的消息队列
- 通过base64字符串之间的编码解码实现图片上传
- 【SSLGZ 1344】Knights
- 机器视觉开源代码集合(转)
- java中==和equals的区别以及java中的堆栈
- POJ
- Maven项目导入到eclipse中
- jmeter.bat启动时提示:'findstr' 不是内部或外部命令,也不是可运行的程序或批处理文件。
- 8.6-全栈Java笔记:Math类和枚举
- C++泛型函数模板类型
- 求1+2+3+...+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)
- 单例模式的几种实现
- Servlet
- opencv文件夹中vc和VS对应关系