2017暑假集訓 div1 連通圖(2)

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;
}












發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章