HDU4738 Caocao's Bridges (求桥)
来源:互联网 发布:孔子学院的数据 编辑:程序博客网 时间:2024/05/17 15:59
题意:
现在有个(可重边)无向图,无向图的每条边上都有一定数目的守卫,你现在想派人去炸掉这个图的一条边,是的该图不连通。但是你只能炸1条边且如果该边守卫为x人,那么你至少要派x个人过去。所以现在问你最少需要派多少人出发?
思路:就是求一个有重边的无向图的桥,有几个比较坑的地方
1,所给的图可能不连通,且不连通的时候不需要炸,输出0
2,当所要去炸的桥上的守卫数=0时,需要输出1而不是0
3,有重边
我的思路很简单,如果有重边的话那么就一定不是桥,那么只需要给一个特别一点值,在dfs里面更新的时候判断如果是这个特别的值就可以跳过了。
这个不能像求联通块那样 直接 !k continue就好了, 因为求割边是 在!dfn时候做的。。含重边求桥两种方法把
#include <iostream>#include <cstring>#include <cstdio>#include <vector>#include <stack>#include <map>#include <algorithm>using namespace std;const int maxn = 1e3 + 5;const int INF = 1e9;int n, m,low[maxn], dfn[maxn], id[maxn], scc_cnt, dfs_cnt, cnt;int g[maxn][maxn], ans, d[maxn][maxn];vector<int> v[maxn];map<int, int> mp;void init(){ memset(low, 0, sizeof(low)); memset(id, 0, sizeof(id)); memset(dfn, 0, sizeof(dfn)); scc_cnt = dfs_cnt = cnt = 0; for(int i = 0; i < maxn; i++) v[i].clear(); memset(g, 0, sizeof(g));}void tarjan(int x, int f){ dfn[x] = low[x] = ++dfs_cnt; int k = 0; for(int i = 0; i < v[x].size(); i++) { int to = v[x][i]; if(to == f && !k) { k++; continue; } if(!dfn[to]) { tarjan(to, x); low[x] = min(low[x], low[to]); if(low[to] > dfn[x] && g[x][to] != -1) ans = min(ans, g[x][to]), cnt++; } else if(dfn[to] < dfn[x]) low[x] = min(low[x], dfn[to]); }}int main(){// freopen("in.txt", "r", stdin); while(~scanf("%d%d",&n, &m), n+m) { init(); int x, y, z; for(int i = 1; i <= m; i++) { scanf("%d%d%d", &x, &y, &z); if(!g[x][y]) { v[x].push_back(y); v[y].push_back(x); g[x][y] = z; g[y][x] = z; } else { g[x][y] = -1; g[y][x] = -1; } } ans = INF; cnt = 0; tarjan(1, -1);// cout << cnt << endl; int flag = 0; for(int i = 1; i <= n; i++) if(!dfn[i]) { flag = 1; break; } if(ans == 0) puts("1"); else if(flag) puts("0"); else if(ans == INF) puts("-1"); else printf("%d\n", ans); } return 0;}
本题的本质还是无向图求桥,且求得是守卫数目最少的那个桥。但是本题有3个点要注意:
1.所给的图可能不连通,且不连通的时候不需要炸,输出0.
2.当所要去炸的桥上的守卫数=0时,我们需要派的人数是1不是0.
3.任意两个节点u与v之间可能存在多条边。
对于上面的1与2点,我们在原始tarjan()函数运行完后加一些判断就能解决.
不过对于重边无向图,首先我们要用邻接表来保存图了(不能再用vector的邻接矩阵了).
然后之前无重边的时候我们都是用过fa来标记父节点的,如果u的儿子等于fa,那么直接跳过。即如果u不通过儿子连回fa的话,low[u]==pre[u]肯定>pre[fa]。现在本题其实u是可以通过另一条(fa,u)的边连回fa的,所以这里即使u不通过儿子连回fa的话,low[u]==也可以==pre[fa]。因为fa通过边1到u,u可以通过边2到fa。
所以本题把无向图转换成有向图来做:
把每条无向边分为两条有向边i与i+1,如果u通过边i到达了v,那么v中必然有一条边是i^1且可以通过该i^1边到u.所以如果在v节点遍历时到达i^1边时,我们直接跳过.
具体实现还是需要体会代码才能清晰.
#include<cstdio>#include<cstring>#include<algorithm>using namespace std;const int maxn=1000+10;const int maxm=2*1000*1000+100;int n,m;int tot;int head[maxn];struct Edge{ int to,next,w;}edges[maxm];void add_edge(int u,int v,int w){ edges[tot]=(Edge){v,head[u],w}; head[u]=tot++; edges[tot]=(Edge){u,head[v],w}; head[v]=tot++;}int pre[maxn],low[maxn];int dfs_clock,point_num;int ans;void tarjan(int u,int E){ low[u]=pre[u]=++dfs_clock; for(int e=head[u];e!=-1;e=edges[e].next) { int v=edges[e].to; if(e==(E^1)) continue; if(!pre[v]) { tarjan(v,e); low[u]=min(low[u],low[v]); if(low[v]>pre[u]) ans=min(ans,edges[e].w); } else low[u]=min(low[u],pre[v]); } point_num++;}int main(){ while(scanf("%d%d",&n,&m)==2&&n) { ans=1000000; dfs_clock=point_num=tot=0; memset(pre,0,sizeof(pre)); memset(head,-1,sizeof(head)); for(int i=0;i<m;i++) { int u,v,w; scanf("%d%d%d",&u,&v,&w); add_edge(u,v,w); } tarjan(1,-1); if(point_num<n) printf("0\n"); //图不连通,不用炸 else if(ans==1000000) printf("-1\n"); //图中无桥 else if(ans==0) printf("%d\n",1); //桥上兵为0 else printf("%d\n",ans); } return 0;}kuangbin板子
/* ***********************************************Author :kuangbinCreated Time :2013/9/15 星期日 12:11:49File Name :2013杭州网络赛\1001.cpp************************************************ */#pragma comment(linker, "/STACK:1024000000,1024000000")#include <stdio.h>#include <string.h>#include <iostream>#include <algorithm>#include <vector>#include <queue>#include <set>#include <map>#include <string>#include <math.h>#include <stdlib.h>#include <time.h>using namespace std;const int INF = 0x3f3f3f3f;/** 求 无向图的割点和桥* 可以找出割点和桥,求删掉每个点后增加的连通块。* 需要注意重边的处理,可以先用矩阵存,再转邻接表,或者进行判重*/const int MAXN = 10010;const int MAXM = 2000010;struct Edge{ int to,next; int w; bool cut;//是否为桥的标记}edge[MAXM];int head[MAXN],tot;int Low[MAXN],DFN[MAXN],Stack[MAXN];int Index,top;bool Instack[MAXN];bool cut[MAXN];int add_block[MAXN];//删除一个点后增加的连通块int bridge;void addedge(int u,int v,int w){ edge[tot].to = v;edge[tot].next = head[u];edge[tot].cut = false; edge[tot].w = w; head[u] = tot++;}void Tarjan(int u,int pre){ int v; Low[u] = DFN[u] = ++Index; Stack[top++] = u; Instack[u] = true; int son = 0; int pre_num = 0; for(int i = head[u];i != -1;i = edge[i].next) { v = edge[i].to; if(v == pre && pre_num == 0) { pre_num++; continue; } if( !DFN[v] ) { son++; Tarjan(v,u); if(Low[u] > Low[v])Low[u] = Low[v]; //桥 //一条无向边(u,v)是桥,当且仅当(u,v)为树枝边,且满足DFS(u)<Low(v)。 if(Low[v] > DFN[u]) { bridge++; edge[i].cut = true; edge[i^1].cut = true; } //割点 //一个顶点u是割点,当且仅当满足(1)或(2) (1) u为树根,且u有多于一个子树。 //(2) u不为树根,且满足存在(u,v)为树枝边(或称父子边, //即u为v在搜索树中的父亲),使得DFS(u)<=Low(v) if(u != pre && Low[v] >= DFN[u])//不是树根 { cut[u] = true; add_block[u]++; } } else if( Low[u] > DFN[v]) Low[u] = DFN[v]; } //树根,分支数大于1 if(u == pre && son > 1)cut[u] = true; if(u == pre)add_block[u] = son - 1; Instack[u] = false; top--;}int solve(int N){ memset(DFN,0,sizeof(DFN)); memset(Instack,false,sizeof(Instack)); memset(add_block,0,sizeof(add_block)); memset(cut,false,sizeof(cut)); Index = top = 0; bridge = 0; for(int i = 1;i <= N;i++) if( !DFN[i] ) Tarjan(i,i); int ret = INF; for(int u = 1; u <= N;u++) for(int i = head[u]; i != -1;i = edge[i].next) if(edge[i].cut) ret = min(ret,edge[i].w); if(ret == INF)ret = -1; if(ret == 0)ret++; return ret;}int F[MAXN];int find(int x){ if(F[x] == -1)return x; else return F[x] = find(F[x]);}void init(){ memset(F,-1,sizeof(F)); tot = 0; memset(head,-1,sizeof(head));}void bing(int u,int v){ int t1 = find(u); int t2 = find(v); if(t1 != t2)F[t1] = t2;}int main(){ //freopen("in.txt","r",stdin); //freopen("out.txt","w",stdout); int n,m; while(scanf("%d%d",&n,&m) == 2) { if(n == 0 && m == 0)break; int u,v,w; init(); while(m--) { scanf("%d%d%d",&u,&v,&w); if(u == v)continue; addedge(u,v,w); addedge(v,u,w); bing(u,v); } bool flag = true; for(int i = 1; i <= n;i++) if(find(i) != find(1)) flag = false; if(!flag) { printf("0\n"); continue; } printf("%d\n",solve(n)); } return 0;}
- HDU4738 Caocao's Bridges (求桥)
- HDU4738 Caocao's Bridges(桥)
- hdu4738 Caocao's Bridges 无向图 tarjan 求桥
- HDU4738-Caocao's Bridges
- HDU4738 Caocao's Bridges
- 【桥】HDU4738[Caocao's Bridges]题
- hdu4738 Caocao's Bridges(双连通分量割边/桥)
- 无向图求桥 hdu4738 Caocao's Bridges
- HDU4738——Caocao's Bridges
- HDU4738 Caocao's Bridges【强连通】
- hdu4638-tarjan求桥-Caocao's Bridges
- Caocao's Bridges hdu4738 (网络赛 杭州赛区) hdu 4738
- [HDU 4738] Caocao's Bridges Tarjan算法求桥
- HDU 4738 Caocao's Bridges(求价值最小的桥)
- HDOJ 题目4738 Caocao's Bridges(双联通,求桥)
- hdu 4738 Caocao's Bridges(Tarjan求无向图的桥)
- hdu 4738 Caocao's Bridges 【求最小代价的割边(桥)】
- hdu 4738 Caocao's Bridges(求割边和桥)
- 欢迎使用CSDN-markdown编辑器
- oj 光棍的悲伤
- Java中的一些重要概念
- 各有千秋,iFunk 旗下产品特点一览
- 使用Mocha+spectron测试Electron打包的桌面版程序(1)
- HDU4738 Caocao's Bridges (求桥)
- 不懂这些SEO技巧,你还敢说你是前端?
- git提示error setting certificate verify locations解决办法
- 阿里云oss
- Makefile 条件判断语句
- Hbase 数据模型
- android Scroller的用法总结
- c++学习2
- mysql 创建,删除,修改索引