您的位置:首页 > 其它

HDU 4738 Caocao's Bridges(重边无向图求桥)

2016-02-04 00:04 357 查看
题意
现在有个(可重边)无向图,无向图的每条边上都有一定数目的守卫,你现在想派人去炸掉这个图的一条边,是的该图不连通。但是你只能炸1条边且如果该边守卫为x人,那么你至少要派x个人过去。所以现在问你最少需要派多少人出发?
思路:就是求一个有重边的无向图的桥,有几个比较坑的地方
1,所给的图可能不连通,且不连通的时候不需要炸,输出0
2,当所要去炸的桥上的守卫数=0时,需要输出1而不是0
3,有重边
我的思路很简单,如果有重边的话那么就一定不是桥,那么只需要给一个特别一点值,在dfs里面更新的时候判断如果是这个特别的值就可以跳过了。在网上搜的题解都是转成有向图的做法,每条无向边转成两条有向边,..我还不是很懂..以后再回来看吧

//*我的代码*//
#include<cstdio>
#include<cstring>
#include<vector>
using namespace std;
const int maxn=1500+10;
const int INF = 100000000;
int ans;
int n,m;
int dfs_clock;//时钟,每访问一个节点增1
vector<int> G[maxn];//G[i]表示i节点邻接的所有节点
int pre[maxn];//pre[i]表示i节点被第一次访问到的时间戳,若pre[i]==0表示i还未被访问
int low[maxn];//low[i]表示i节点及其后代能通过反向边连回的最早的祖先的pre值
bool iscut[maxn];//标记i节点是不是一个割点
int adj[maxn][maxn];
int pointnum;
//求出以u为根节点(u在DFS树中的父节点是fa)的树的所有割顶和桥
//初始调用为dfs(root,-1);
int dfs(int u,int fa)
{
int lowu=pre[u]=++dfs_clock;
int child=0;    //子节点数目
for(int i=0; i<G[u].size(); i++)
{
int v=G[u][i];
if(!pre[v])
{
child++;//未访问过的节点才能算是u的孩子
int lowv=dfs(v,u);
lowu=min(lowu,lowv);
if(lowv>=pre[u])
{
iscut[u]=true;      //u点是割顶
if(lowv>pre[u] && adj[u][v]!=-2)   //(u,v)边是桥
ans = min(ans,adj[u][v]);
}
}
else if(pre[v]<pre[u] && v!=fa)//v!=fa确保了(u,v)是从u到v的反向边
{
lowu=min(lowu,pre[v]);
}
}
if(fa<0 && child==1 )
iscut[u]=false;//u若是根且孩子数<=1,那u就不是割顶
pointnum++;
return low[u]=lowu;
}

int main()
{
while(scanf("%d%d",&n,&m)==2&&n)
{
dfs_clock=0;//初始化时钟
memset(pre,0,sizeof(pre));
memset(iscut,0,sizeof(iscut));
memset(adj,-1,sizeof(adj));
for(int i=0;i<=n;i++) G[i].clear();
for(int i=0;i<m;i++)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
//   G[u].push_back(v);
//   G[v].push_back(u);
if (adj[u][v]==-1)
{
G[u].push_back(v);
G[v].push_back(u);
adj[u][v]=w;
adj[v][u]=w;
}
else
{
adj[u][v]=-2;
adj[v][u]=-2;
}
}
ans = INF;
pointnum=0;
int flag=0;
dfs(1,-1);//初始调用
for (int i = 1;i<=n;i++)  //是否连通
if (!pre[i])
{
flag=1;
break;
}
/*if (pointnum < n)
{
flag = 1;
}*/
if (flag) printf("0\n");
else if (ans == INF)
printf("-1\n");
else if (ans==0)
printf("1\n");
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;
}


Description

Caocao was defeated by Zhuge Liang and Zhou Yu in the battle of Chibi. But he wouldn't give up. Caocao's army still was not good at water battles, so he came up with another idea. He built many islands in the Changjiang river, and
based on those islands, Caocao's army could easily attack Zhou Yu's troop. Caocao also built bridges connecting islands. If all islands were connected by bridges, Caocao's army could be deployed very conveniently among those islands. Zhou Yu couldn't stand
with that, so he wanted to destroy some Caocao's bridges so one or more islands would be seperated from other islands. But Zhou Yu had only one bomb which was left by Zhuge Liang, so he could only destroy one bridge. Zhou Yu must send someone carrying the
bomb to destroy the bridge. There might be guards on bridges. The soldier number of the bombing team couldn't be less than the guard number of a bridge, or the mission would fail. Please figure out as least how many soldiers Zhou Yu have to sent to complete
the island seperating mission.

Input

There are no more than 12 test cases.

In each test case:

The first line contains two integers, N and M, meaning that there are N islands and M bridges. All the islands are numbered from 1 to N. ( 2 <= N <= 1000, 0 < M <= N 2 )

Next M lines describes M bridges. Each line contains three integers U,V and W, meaning that there is a bridge connecting island U and island V, and there are W guards on that bridge. ( U ≠ V and 0 <= W <= 10,000 )

The input ends with N = 0 and M = 0.

Output

For each test case, print the minimum soldier number Zhou Yu had to send to complete the mission. If Zhou Yu couldn't succeed any way, print -1 instead.

Sample Input

3 3
1 2 7
2 3 4
3 1 4
3 2
1 2 7
2 3 4
0 0


Sample Output

-1
4
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: