您的位置:首页 > 其它

2017暑假集训 div1 连通图(2)

2017-07-17 22:09 363 查看
HDU  4612

题意,给一张图,问加一条边之后最小剩多少桥

做法(参考大神):直接算出原始桥的个数减去缩点后树的直径(啥玩意啊)

树的直径:树上最长的一条路的长度。 就是从最深点经过root到次深点那条路的长度

具体的做法就是:先bfs出最后出队列的点,然后以该点为起点再bfs一遍,这次最后出队列的dis值就是直径

#include <iostream>
#include <stdio.h>
#include <queue>
#include <algorithm>
#include <stack>
#include <string.h>
using namespace std;
const int maxn=210000;
const int maxm=2100000;
const int inf=0x3f3f3f3f;
struct node
{
int to,next;
};
int head[maxn],cnt;
int head1[maxn],cnt1;
struct node edge[maxm];
struct node edge1[maxm];
int scc[maxn] , low[maxn] , dfn[maxn];
int d_cnt , s_cnt,n,m;
stack<int>mys;
void add(int u,int v)
{
edge[cnt].to=v; edge[cnt].next=head[u]; head[u]= cnt++;
}
void add1(int u,int v)
{
edge1[cnt1].to=v; edge1[cnt1].next=head1[u]; head1[u]= cnt1++;
}

void init()
{
memset(head,-1,sizeof(head)); cnt=0;
memset(head1,-1,sizeof(head1)); cnt1=0;
d_cnt=0; s_cnt=0;
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
memset(scc,0,sizeof(scc));
}
void tarjan(int u,int pre)
{
low[u] = dfn[u] = ++d_cnt; mys.push(u);
int flag=0;
for(int i=head[u]; i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(!dfn[v])
{
tarjan(v,u);
low[u]=min(low[u],low[v]);
}
else
{
if(v==pre)
{
if(flag) low[u]=min(low[u],dfn[v]); //重边
flag++;
}
else low[u]=min(low[u],dfn[v]);
}
}
if(low[u]==dfn[u])
{
s_cnt++;
while(1)
{
int x=mys.top(); mys.pop();
scc[x]=s_cnt;
if(x==u) break;
}
}

}
int dis[maxn];
int spfa(int s)
{
int end=0;
memset(dis,-1,sizeof(dis));
dis[s]=0;
queue<int> que;
que.push(s);
while(!que.empty())
{
int u=que.front() ; que.pop();
end=u;
for(int i=head1[u];i!=-1;i=edge1[i].next)
{
int v=edge1[i].to;
if(dis[v]==-1)
{
dis[v]=dis[u]+1;
que.push(v);
}
}
}
return end;
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0&&m==0) break;
init();
int a,b;
for(int i=1;i<=m;++i)
{
scanf("%d%d",&a,&b);
if(a==b) continue;
add(a,b);
add(b,a);
}
tarjan(1,-1);
for(int i=1;i<=n;++i)
{
for(int j=head[i];j!=-1;j=edge[j].next)
{
int v=edge[j].to;
if(scc[i]!=scc[v])
{
add1(scc[i],scc[v]);
}
}
}
int temp= dis[spfa(spfa(1))];
printf("%d\n",s_cnt-temp-1);
}
return 0;
}

HDU 4738

题意:曹操在长江上建立了一些点,点之间有一些边连着。如果这些点构成的无向图变成了连通图,那么曹操就无敌了。刘备为了防止曹操变得无敌,就打算去摧毁连接曹操的点的桥。但是诸葛亮把所有炸弹都带走了,只留下一枚给周瑜。所以周瑜只能炸一条桥。题目给出n,m。表示有n个点,m条桥。接下来的m行每行给出a,b,c,表示a点和b点之间有一条桥,而且曹操派了c个人去守卫这条桥。现在问周瑜最少派多少人去炸桥。如果无法使曹操的点成为多个连通图,则输出-1

做法:本来以为是道好欺负的题,结果各种坑点

1.如果本身图就不连通输出0

2.如果不存在桥就输出-1

3.输出权值最小的(但这有个巨坑,如果权值为0的话,还得输出1,因为要派一个人去炸桥!!!)

4.注意强联通时对重边进行处理

#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
const int inf=0x3f3f3f3f;
const int maxn=3015;
struct node
{
int to,next,w;
}edge[maxn*maxn];
int head[maxn], cnt=0;
int num=0;
int dfn[maxn],low[maxn];
int ans,n,m,d_cnt;
void add(int u,int v,int w)
{
edge[cnt].to=v; edge[cnt].w=w;
edge[cnt].next=head[u]; head[u]=cnt++;
}
void dfs(int u,int pre)
{
dfn[u]=low[u]= ++d_cnt;
int t=0;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(!dfn[v])
{
dfs(v,u);
low[u]=min(low[u],low[v]);
if(low[v]>dfn[u])
{
ans=min(ans,edge[i].w);
}
}
else
{
if(v==pre)
{
if(t) low[u]=min(low[u],dfn[v]);
t++;
}
else low[u]=min(low[u],dfn[v]);
}
}
}
void tarjan()
{
ans=inf;
memset(low,0,sizeof(low));
memset(dfn,0,sizeof(dfn));
d_cnt=0;
for(int i=1;i<=n;++i) if(!dfn[i]) dfs(i,-1);
if(ans==inf) printf("-1\n");
else if(ans==0) printf("1\n");
else printf("%d\n",ans);
}
bool vis[maxn];
void f(int u)
{
vis[u]=1;
for(int i=head[u];i!=-1;i=edge[i].next)
{
int v=edge[i].to;
if(!vis[v])
{
f(v);
}
}
}
int main()
{
while(scanf("%d%d",&n,&m)!=EOF)
{
if(n==0&&m==0) break;
memset(head,-1,sizeof(head));
memset(vis,0,sizeof(vis));
cnt=0;
for(int i=1;i<=m;++i)
{
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
add(u,v,w); add(v,u,w);
}
f(1);
int flag=0;
for(int i=1;i<=n;++i) flag+=(int)vis[i];
if(flag<n)
{
printf("0\n");
continue;
}
tarjan();
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: