1. 程式人生 > >洛谷——P3225 [HNOI2012]礦場搭建

洛谷——P3225 [HNOI2012]礦場搭建

add spa case || ace sizeof 聯通塊 組成 無向圖

P3225 [HNOI2012]礦場搭建

題目描述

煤礦工地可以看成是由隧道連接挖煤點組成的無向圖。為安全起見,希望在工地發生事故時所有挖煤點的工人都能有一條出路逃到救援出口處。於是礦主決定在某些挖煤點設立救援出口,使得無論哪一個挖煤點坍塌之後,其他挖煤點的工人都有一條道路通向救援出口。

請寫一個程序,用來計算至少需要設置幾個救援出口,以及不同最少救援出口的設置方案總數。

輸入輸出格式

輸入格式:

輸入文件有若幹組數據,每組數據的第一行是一個正整數 N(N<=500),表示工地的隧道數,接下來的 N 行每行是用空格隔開的兩個整數 S 和 T,表示挖 S 與挖煤點 T 由隧道直接連接。輸入數據以 0 結尾。

輸出格式:

輸入文件中有多少組數據,輸出文件 output.txt 中就有多少行。每行對應一組輸入數據的 結果。其中第 i 行以 Case i: 開始(註意大小寫,Case 與 i 之間有空格,i 與:之間無空格,: 之後有空格),其後是用空格隔開的兩個正整數,第一個正整數表示對於第 i 組輸入數據至少需 要設置幾個救援出口,第二個正整數表示對於第 i 組輸入數據不同最少救援出口的設置方案總 數。輸入數據保證答案小於 2^64。輸出格式參照以下輸入輸出樣例。

輸入輸出樣例

輸入樣例#1:
9
1  3
4  1
3  5
1  2
2  6
1  5
6  3
1  6
3  2
6
1  2
1  3
2  4
2  5
3  6
3  7
0
輸出樣例#1:
Case 1: 2 4
Case 2: 4 1

說明

Case 1 的四組解分別是(2,4),(3,4),(4,5),(4,6);

Case 2 的一組解為(4,5,6,7)。

分情況討論:

很顯然是要求割點嘛、、、(分類上寫著、、、)

好吧,求完割點以後幹什麽??

我一直認為是先求出割點,然後將割點全部炸掉(屏蔽)後求出每個聯通塊的點的個數,然後在用乘法原理來做。

結果發現全wa!!!(ORZ)

據說這道題要分類討論、、、、

①若一個雙聯通中沒有割點,那麽最少需要兩個出口(炸了一個去另一個)

②若一個雙聯通能連到一個割點,那麽最少需要在雙聯通中設置一個出口,炸了割點還能逃生、

③若一個雙聯通能連到多余一個割點,那麽不管怎麽炸,都能從沒炸的割點跑到別的分量裏

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 510
using namespace std;
long long ans2;
bool vis[N],cut_point[N];
int n,m,x,y,s,tot,tim,cnt,cut,sum,ans1;
int dfn[N],low[N],head[N],belong[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<0||ch>9){if(ch==-)f=-1; ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0; ch=getchar();}
    return x*f;
}
struct Edge
{
    int from,to,next;
}edge[N<<1];
int add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
int begin()
{
    memset(vis,0,sizeof(vis));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(edge,0,sizeof(edge));
    memset(head,0,sizeof(head));
    memset(belong,0,sizeof(belong));
    memset(cut_point,0,sizeof(cut_point));
    n=0,tot=1,ans1=0,ans2=1,tim=0,sum=0;
}
int tarjan(int now,int pre)
{
    dfn[now]=low[now]=++tim;
    int tt=0; bool boo=false;
    for(int i=head[now];i;i=edge[i].next)
    {
        int t=edge[i].to;
        if((1^i)==pre) continue;
        if(!dfn[t])
        {
            tt++;tarjan(t,i);
            low[now]=min(low[now],low[t]);
            if(dfn[now]<=low[t]) boo=true;
        }
        else low[now]=min(low[now],dfn[t]);
    }
    if(pre==-1) {if(tt>1) cut_point[now]=true;}
    else if(boo) cut_point[now]=true; 
}
int dfs(int x)
{
    s++;
    vis[x]=true;
    belong[x]=sum;
    for(int i=head[x];i;i=edge[i].next)
    {
        int t=edge[i].to;
        if(cut_point[t]&&belong[t]!=sum) cut++,belong[t]=sum;
        if(!vis[t]&&!cut_point[t]) dfs(t);
    }
}
int main()
{
    while(1)
    {
        m=read();if(m==0) break;
        begin();
        for(int i=1;i<=m;i++)
        {
            x=read(),y=read();
            add(x,y),add(y,x);
            n=max(max(x,y),n);
        }
        for(int i=1;i<=n;i++)
         if(!dfn[i]) tarjan(i,-1);
        for(int i=1;i<=n;i++)
        {
            if(vis[i]||cut_point[i]) continue;
            s=0,cut=0;
            sum++;dfs(i);
            if(!cut) ans1+=2,ans2*=(long long)(s*(s-1)>>1);
            if(cut==1) ans1++,ans2*=(long long)s;
        }
        printf("Case %d: %d %lld\n",++cnt,ans1,ans2);
    }
    return 0;
}

傻不拉幾的只考慮一種情況還調試半天、、、、結果全wa

技術分享
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<iostream>
#include<algorithm>
#define N 11000
using namespace std;
bool vis[N],cut_point[N];
int n,m,tot,tim,sum,cnt,top,ans1,ans2;
int x[N],y[N],dfn[N],low[N],ans[N],head[N],stack[N],belong[N];
int read()
{
    int x=0,f=1; char ch=getchar();
    while(ch<0||ch>9){if(ch==-)f=-1; ch=getchar();}
    while(ch>=0&&ch<=9){x=x*10+ch-0; ch=getchar();}
    return x*f;
}
struct Edge
{
    int from,to,next;
}edge[N];
int add(int x,int y)
{
    tot++;
    edge[tot].to=y;
    edge[tot].next=head[x];
    head[x]=tot;
}
int tarjan(int x,int pre)
{
    int s=0; bool boo;
    dfn[x]=low[x]=++tim;
    vis[x]=true;
    for(int i=head[x];i;i=edge[i].next)
    {
        int t=edge[i].to;
        if((1^i)==pre) continue;
         if(!vis[t])
        {
            s++;tarjan(t,i);
            low[x]=min(low[t],low[x]);
            if(low[t]>=dfn[x]) boo=true;  
        }
        else low[x]=min(low[x],dfn[t]);
    }
    if(!pre) {if(s>1) cut_point[x]=true;}
    else {if(boo) cut_point[x]=true;}
}
int tarjan1(int now)
{
    dfn[now]=low[now]=++tim;
    stack[++top]=now;vis[now]=true;
    for(int i=head[now];i;i=edge[i].next)
    {
        int t=edge[i].to;
        if(vis[t]) low[now]=min(low[now],dfn[t]);
        else if(!dfn[t]) tarjan1(t),low[now]=min(low[now],low[t]);
    }
    if(dfn[now]==low[now])
    {
        sum++; belong[now]=sum;ans[sum]++;
        for(;stack[top]!=now;top--)
         belong[stack[top]]=sum,vis[stack[top]]=false,ans[sum]++;
        vis[now]=false; top--;
    }
}
int begin()
{
    tot=0,tim=0,sum=0,ans2=1;
    memset(ans,0,sizeof(ans));
    memset(dfn,0,sizeof(dfn));
    memset(low,0,sizeof(low));
    memset(vis,0,sizeof(vis));
    memset(head,0,sizeof(head));
    memset(belong,0,sizeof(belong));
}
int main()
{
    while(1)
    {
        m=read();if(m==0) break;
        begin();cnt++,tot=1;
        memset(cut_point,0,sizeof(cut_point));
        for(int i=1;i<=m;i++)
          x[i]=read(),y[i]=read(),add(x[i],y[i]),add(y[i],x[i]);    
        tarjan(1,0); begin();
        for(int i=1;i<=m;i++)
        {
            n=max(max(x[i],y[i]),n);
            if(cut_point[x[i]]||cut_point[y[i]]) continue;
            add(x[i],y[i]),add(y[i],x[i]); 
        }
        for(int i=1;i<=n;i++)
         if(!dfn[i]&&!cut_point[i]) tarjan1(i);
        ans1=sum;
        for(int i=1;i<=sum;i++)
          ans2*=ans[i];
        printf("Case %d: %d %d\n",cnt,ans1,ans2);
    }
    return 0;
}
View Code

洛谷——P3225 [HNOI2012]礦場搭建