连通问题

HDU 1269

题意:

如果图中任意两点可以互相到达输出Yes,反之No

解析:

判断强连通分量是否为1

ac:

#include<bits/stdc++.h>
#define MAXN 100005
using namespace std;
#define inf 0x3f3f3f3f

vector<int> g[MAXN];
int color[MAXN],dfn[MAXN],low[MAXN],stck[MAXN],vis[MAXN];
int deep,top,n,m,sum,a,b;

void tarjan(int u)
{
    dfn[u]=++deep;
    low[u]=deep;
    vis[u]=1;
    stck[++top]=u;
    int sz=g[u].size();
    for(int i=0;i<sz;i++)
    {
        int v=g[u][i];
        if(!dfn[v])
        {
            tarjan(v);
            low[u]=min(low[u],low[v]);
        }
        else
        {
            if(vis[v])
                low[u]=min(low[u],low[v]);
        }
    }
    if(dfn[u]==low[u])
    {
        color[u]=++sum;
        vis[u]=0;
        while(stck[top]!=u)
        {
            color[stck[top]]=sum;
            vis[stck[top--]]=0;
        }
        top--;
    }
}

void init()
{
    deep=top=sum=0;
    for(int i=0;i<=MAXN;i++)
        g[i].clear();
    memset(color,0,sizeof(color));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    memset(stck,0,sizeof(stck));
}

int main()
{
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        if(n==0&&m==0)
            break;
        init();
        for(int i=1;i<=m;i++)
        {
            scanf("%d%d",&a,&b);
            g[a].push_back(b);
        }
        for(int i=1;i<=n;i++)
            if(!dfn[i])
                tarjan(i);
        if(sum>1)
            printf("No\n");
        else
            printf("Yes\n");
    }
}

https://vjudge.net/problem/POJ-3177

题意:

给定一个无向连通图,问加多少条边可以使得图变成一个边双连通图

解析:

先求出边双连通,然后缩点,缩点后,将叶子结点相连

结果为:(ans+1)/2;(ans:叶子结点数目)

ac:

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
#define MAXN 200005
using namespace std;
int to[MAXN<<1],nxt[MAXN<<1],head[MAXN<<1];
int tot,cnt;
int dfn[MAXN],low[MAXN];
int vis[MAXN<<1];

void init()
{
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    memset(head,0,sizeof(head));
    tot=1,cnt=0;
}

void add(int u,int v)
{
    to[++tot]=v;
    nxt[tot]=head[u];
    head[u]=tot;
}

void tarjan(int x,int fa)
{
    dfn[x]=low[x]=++cnt;
    for(int i=head[x];i;i=nxt[i])
    {
        int v=to[i];
        if(dfn[v]==0)
        {
            tarjan(v,i);
            low[x]=min(low[x],low[v]);
            if(low[v]>dfn[x]){
                vis[i]=vis[i^1]=1;
            }
        }
        else if(i!=(fa^1)){
            low[x]=min(low[x],dfn[v]);
        }
    }
}
int u[MAXN<<1],v[MAXN<<1];
vector<int> vc[MAXN];

int c[MAXN],dcc;

void dfs(int x)
{
    c[x]=dcc;
    for(int i=0;i<vc[x].size();i++)
    {
        int v=vc[x][i];
        if(c[v]||vis[i])
            continue;
        dfs(v);
    }
}
int in[MAXN];

int main()
{
    int n,m;
    init();
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d",&u[i],&v[i]),add(u[i],v[i]),add(v[i],u[i]);
    for(int i=1;i<=m;i++)
    {
        if(dfn[i]==0)
            tarjan(i,0);
    }
    for(int i=2;i<=tot;i++)
    {
        if(vis[i]==0){
            vc[to[i^1]].push_back(to[i]);
        }
    }
    dcc=0;
    for(int i=1;i<=n;i++)
    {
        if(c[i]==0){
            ++dcc;
            dfs(i);
        }
    }
    for(int i=2;i<=tot;i++)
    {
        int x=to[i^1],y=to[i];
        if(c[x]==c[y])
            continue;
        in[c[x]]++;
        in[c[y]]++;
    }
    int ans=0;
    for(int i=1;i<=n;i++)
    {
        if(in[i]==2)
            ans++;
    }
    printf("%d\n",(ans+1)/2);
    return 0;
}

https://www.luogu.org/problem/P2341

题意:

将图缩成若干强连通分量

如果一个强连通分量的入度为1,且仅有1个,呢么这个强连通分量里的个数就是答案

ac:

#include<bits/stdc++.h>
#define MAXN 200005
using namespace std;
int to[MAXN<<1],nxt[MAXN<<1],head[MAXN<<1];
int tot,cnt;
int low[MAXN],dfn[MAXN],vis[MAXN];
stack<int> st;
int stck[MAXN<<1],top;
int c[MAXN],in[MAXN],num;
int sz[MAXN];//对连通分量计数

void init()
{
    tot=1;
    top=num=cnt=0;
    memset(head,0,sizeof(head));
}

void add(int u,int v)
{
    to[++tot]=v;
    nxt[tot]=head[u];
    head[u]=tot;
}

void tarjan(int x)
{
    dfn[x]=low[x]=++cnt;
    vis[x]=1;
    st.push(x);
    for(int i=head[x];i;i=nxt[i])
    {
        int v=to[i];
        if(dfn[v]==0)
        {
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else{
            if(vis[v]!=0){
                low[x]=min(low[x],dfn[v]);
            }
        }
    }
    if(dfn[x]==low[x])
    {
        int k;
        ++num;
        do{
            k=st.top();st.pop();
            vis[k]=0;
            c[k]=num;
            sz[num]++;
        }while(x!=k);
    }
}

int main()
{
    init();
    int n,m,u,v;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
        scanf("%d%d",&u,&v),add(u,v);
    for(int i=1;i<=n;i++)
    {
        if(dfn[i]==0)
            tarjan(i);
    }
    for(int i=1;i<=n;i++){//遍历每条边,求每一连通分量的入度
        for(int j=head[i];j;j=nxt[j]){
            int v=to[j];
            if(c[i]!=c[v])
                in[c[i]]++;
        }
    }
    int ans=0,tt=0;
    for(int i=1;i<=num;i++)//入度为0且数量仅为1
    {
        if(in[i]==0)
        {
            tt++;
            ans=sz[i];
        }
    }
    if(tt==1)
        printf("%d\n",ans);
    else
        printf("0\n");
    return 0;
}

http://acm.hdu.edu.cn/showproblem.php?pid=1827

题意:

他想着得快点把这消息告诉大家,虽然他手上有所有人的联系方式,但是一个一个联系过去实在太耗时间和电话费了。

他知道其他人也有一些别人的联系方式,这样他可以通知其他人,再让其他人帮忙通知一下别人。

你能帮Wiskey计算出至少要通知多少人,至少得花多少电话费就能让所有人都被通知到吗?

解析:

我必须通知一些人,这些人打电话给其他人,最后通知了所以人,尽可能的选择必须的且在必须的里选择最小的

对于同一强连通分量,我可以选择里面的任何人,我应该选择里面最小的.

如果一个连通分量能被另外一个连通分量通知,我就不必通知这个连通分量,因为我要通知所以人,所以另一个连通分量应该被先通知.

将图缩点,将同一连通分量缩点,保存同一连通分量里val最小的,然后计算他们的度数,我们选择入度为0,入度为0的只能被我通知

ac:

#include<bits/stdc++.h>
#define ll long long
#define MAXN 2005
using namespace std;
int to[MAXN<<1],head[MAXN<<1],nxt[MAXN<<1];
int tot;
int val[MAXN];
int dfn[MAXN],low[MAXN],cnt,vis[MAXN];
int c[MAXN];
int in[MAXN];
int com[MAXN],num;
stack<int> sk;

void init()
{
    num=cnt=0;
    tot=1;
    for(int i=0;i<MAXN;i++)
        com[i]=1e9;
    memset(head,0,sizeof(head));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    memset(in,0,sizeof(in));
    while(sk.size())
        sk.pop();
}

void add(int u,int v)
{
    to[++tot]=v;
    nxt[tot]=head[u];
    head[u]=tot;
}

void tarjan(int x)
{
    low[x]=dfn[x]=++cnt;
    sk.push(x);
    vis[x]=1;
    for(int i=head[x];i;i=nxt[i])
    {
        int v=to[i];
        if(dfn[v]==0){
            tarjan(v);
            low[x]=min(low[x],low[v]);
        }
        else if(vis[v]!=0){
            low[x]=min(low[x],dfn[v]);
        }
    }
    if(dfn[x]==low[x])
    {
        int k;
        ++num;
        do{
            k=sk.top();
            sk.pop();
            c[k]=num;
            com[num]=min(com[num],val[k]);
            vis[k]=0;
        }while(k!=x);
    }
}

int u[MAXN],v[MAXN];

int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        init();
        for(int i=1;i<=n;i++)
            scanf("%d",&val[i]);
        for(int i=1;i<=m;i++)
            scanf("%d%d",&u[i],&v[i]),add(u[i],v[i]);
        for(int i=1;i<=n;i++)
        {
            if(dfn[i]==0)
                tarjan(i);
        }
        for(int i=1;i<=m;i++)
        {
            if(c[u[i]]!=c[v[i]])
                in[c[v[i]]]++;
        }
        int kk=0;
        ll ans=0;
        for(int i=1;i<=num;i++)
        {
            if(in[i]==0)
            {
                kk++;
                ans+=com[i];
            }
        }
        printf("%d %lld\n",kk,ans);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值