题目
给一张n(n<=1e3)点m(m<=n*n)边图,第i条边上有wi个人(0<=wi<=1e4)
最多炸掉这张图的一条边,其代价是派出的人数不小于边上的人数
使图分裂成两个及以上联通块,最小化派出的人数
思路来源
https://www.cnblogs.com/kuangbin/p/3323369.html
题解
求价值最小的桥,注意几个地方
①由于m较大,有重边,链式前向星能解决,但注意搜桥时不能走反向边
②如果有两个及以上连通块,说明不需要炸,输出0
③如果桥上没人(即桥的边权为0),但要至少派一人才能炸掉,输出1
代码
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=1e3+10;
const int maxm=maxn*maxn*2;//双向边
int n,m,head[maxn];
int cnt=1;//注意第一条边为2才可 2^3
int dfn[maxn],low[maxn],num,tot;
bool bridge[maxm];
int u,v,w,ans;
struct edge
{
int to,next,w;
}e[maxm];
void init()
{
memset(head,0,sizeof head);
memset(bridge,0,sizeof bridge);
memset(dfn,0,sizeof dfn);
memset(low,0,sizeof low);
cnt=1;ans=INF;tot=0;
}
void add(int u,int v,int w)
{
e[++cnt].to=v;
e[cnt].w=w;
e[cnt].next=head[u];
head[u]=cnt;
}
void dfs(int u,int in)
{
low[u]=dfn[u]=++num;
for(int i=head[u];i;i=e[i].next)
{
int v=e[i].to;
if(!dfn[v])
{
dfs(v,i);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u])
{
bridge[i]=bridge[i^1]=1;
ans=min(ans,e[i].w);
}
}
else if(i!=(in^1))
low[u]=min(low[u],dfn[v]);
}
}
int main()
{
while(~scanf("%d%d",&n,&m))
{
if(n==0&&m==0)break;
init();
for(int i=1;i<=m;++i)
{
scanf("%d%d%d",&u,&v,&w);
add(u,v,w);
add(v,u,w);
}
for(int i=1;i<=n;++i)
if(!dfn[i])tot++,dfs(i,0);
if(tot>1)puts("0");
else if(ans==INF)puts("-1");
else if(ans==0)puts("1");
else printf("%d\n",ans);
}
return 0;
}