1. 程式人生 > >洛谷 P2746 [USACO5.3]校園網Network of Schools 解題報告

洛谷 P2746 [USACO5.3]校園網Network of Schools 解題報告

include print 正整數 討論 math cst code 格式 一個

P2746 [USACO5.3]校園網Network of Schools

題目描述

一些學校連入一個電腦網絡。那些學校已訂立了協議:每個學校都會給其它的一些學校分發軟件(稱作“接受學校”)。註意即使 B 在 A 學校的分發列表中, A 也不一定在 B 學校的列表中。

你要寫一個程序計算,根據協議,為了讓網絡中所有的學校都用上新軟件,必須接受新軟件副本的最少學校數目(子任務 A)。更進一步,我們想要確定通過給任意一個學校發送新軟件,這個軟件就會分發到網絡中的所有學校。為了完成這個任務,我們可能必須擴展接收學校列表,使其加入新成員。計算最少需要增加幾個擴展,使得不論我們給哪個學校發送新軟件,它都會到達其余所有的學校(子任務 B)。一個擴展就是在一個學校的接收學校列表中引入一個新成員。

輸入輸出格式

輸入格式:

輸入文件的第一行包括一個整數 N:網絡中的學校數目\((2<=N<=100)\)。學校用前 N 個正整數標識。

接下來 N 行中每行都表示一個接收學校列表(分發列表)。第 i+1 行包括學校 i 的接收學校的標識符。每個列表用 0 結束。空列表只用一個 0 表示。

輸出格式:

你的程序應該在輸出文件中輸出兩行。

第一行應該包括一個正整數:子任務 A 的解。

第二行應該包括子任務 B 的解。


第一步顯然要縮點。。。

第二步統計新點每個點的入度和初度。

第一問顯然就是有多少點入度為0,把機子放到那些點就行了。第二問我們把入度和初度為0的點連起來造環,取數量大的那個。

值得一提的是,縮完了以後是單點的情況要額外討論喵


code:

#include <cstdio>
int min(int x,int y){return x<y?x:y;}
int max(int x,int y){return x>y?x:y;}
const int N=101;
int n;
struct Edge
{
    int to,next;
}edge[N*N];
int head[N],cnt=0;
void add(int u,int v)
{
    edge[++cnt].next=head[u];edge[cnt].to=v;head[u]=cnt;
}
int low[N],dfn[N],is[N],in[N],out[N],s[N],ha[N],tot=0,time=0,n0=0;
void push(int x){s[++tot]=x;}
void pop(){tot--;}
void tarjan(int now)
{
    low[now]=dfn[now]=++time;
    is[now]=1;
    push(now);
    for(int i=head[now];i;i=edge[i].next)
    {
        int v=edge[i].to;
        if(!dfn[v])
        {
            tarjan(v);
            low[now]=min(low[now],low[v]);
        }
        else if(is[v])
            low[now]=min(low[now],dfn[v]);
    }
    if(low[now]==dfn[now])
    {
        n0++;int k;
        do
        {
            k=s[tot];
            pop();
            is[k]=0;
            ha[k]=n0;
        }while(k!=now);
    }
}
int main()
{
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        int v;
        scanf("%d",&v);
        while(v)
        {
            add(i,v);
            scanf("%d",&v);
        }
    }
    for(int i=1;i<=n;i++)
        if(!dfn[i])
            tarjan(i);
    for(int i=1;i<=n;i++)
        for(int j=head[i];j;j=edge[j].next)
        {
            int v=edge[j].to;
            if(ha[v]!=ha[i])
                in[ha[v]]++,out[ha[i]]++;
        }
    int ans1=0,ans2=0;
    if(n0==1) {printf("1\n0\n");return 0;}
    for(int i=1;i<=n0;i++)
    {
        if(!in[i]) ans1++;
        if(!out[i]) ans2++;
    }
    printf("%d\n%d\n",ans1,max(ans2,ans1));
    return 0;
}

2018.6.10

洛谷 P2746 [USACO5.3]校園網Network of Schools 解題報告