1. 程式人生 > 其它 >[NOIP2013 普及組] 車站分級

[NOIP2013 普及組] 車站分級

原題連結

分析

這題還是比較好分析的

利用了,逆向思維。

當我們知道一個車次中所有能停靠的站後,通過題目可知,對停靠的每個點而言,沒有停靠的一定不大於停靠點。

因此就好辦了,我們知道每個車次之後,將所有的非停靠點和停靠點之間連一個邊。

因為題目保證一定有解,則一定是無環的,因此直接跑一遍拓撲序即可。

兩種存圖方式

鄰接矩陣

我們可以注意到,該題的邊數最多有,5005001000,如果用連結串列直接儲存的話,直接就會爆炸。

所有我的想法是,就存一個鄰接矩陣嘛,問題也不大

Ac_code

#include<bits/stdc++.h>
using namespace std;
const int N = 1010;
int stop[N],d[N],f[N];
bool st[N],g[N][N];
int n,m;

template < typename T >
inline void read(T &x)
{
    x = 0; bool f = 0; char ch = getchar();
    while(!isdigit(ch)){f ^= !(ch ^ 45);ch=getchar();}
    while(isdigit(ch)) x= (x<<1)+(x<<3)+(ch&15),ch=getchar();
    x = f ? -x : x;
}

void topsort()
{
    queue<int> q;
    for(int i=1;i<=n;i++)   
        if(!d[i])
        {
            q.push(i);
            f[i]=1;
            //cout<<i<<endl;
        }
    while(q.size())
    {
        auto t = q.front();
        q.pop();
        for(int i=1;i<=n;i++)
            if(g[t][i])
            {
                if(--d[i]==0) 
                    q.push(i);
                f[i]=max(f[i],f[t]+1);
            }
    }
}

int main()
{
    read(n),read(m);
    while(m--)
    {
        for(int i=1;i<=n;i++) st[i]=0;
        int t,mmin=n,mmax = 1,cnt=0;
        read(t);
        while(t--)
        {
            int x;
            read(x);
            stop[cnt++]=x;
            st[x]=1;
            mmin=min(mmin,x);
            mmax=max(mmax,x);
        }
        for(int i=0;i<cnt;i++)
            for(int j=mmin;j<=mmax;j++)
                if(!st[j]&&!g[j][stop[i]]){//這裡卡了我半天,emmm,如果有重邊,那要排除掉
                    g[j][stop[i]]=1,d[stop[i]]++;
                }
    }
    topsort();
    int ans = 0;
    for(int i=1;i<=n;i++) 
        ans=max(ans,f[i]);
    cout<<ans<<endl;
}

鄰接表

這裡如果直接用鄰接表的話會直接炸掉。

所以我們,用了一個小技巧。

如果想在兩個集合終點任意兩點之間連一條邊,那可以在兩個集合中間,建一個虛擬點,將左邊所有點,連線一個邊權為0的邊到虛擬點上,再從虛擬點連線邊權為1的邊到右邊的集合中所有點。

這樣邊數,就從nm變為了n+m

#include<bits/stdc++.h>
using namespace std;
const int N = 2e3 + 10,M = 2e6 + 10;
int h[N],e[M],ne[M],w[M],idx;
int q[N],d[N];
bool st[N];
int dist[N];
int n,m;

void add(int a,int b,int c)
{
    e[idx]=b,ne[idx]=h[a],w[idx]=c,h[a]=idx++,d[b]++;
}

void topsort()
{
    int hh=0,tt=-1;
    for(int i=1;i<=n+m;i++)
        if(!d[i]) 
            q[++tt]=i;
    while(hh<=tt)
    {
        int t = q[hh++];
        for(int i = h[t];~i;i=ne[i])
        {
            int j = e[i];
            if(--d[j]==0)
                q[++tt]=j;
        }
    }
}
int main()
{
    scanf("%d%d",&n,&m);
    memset(h,-1,sizeof h);
    for(int i=1;i<=m;i++)
    {
        memset(st, 0, sizeof st);
        int s;
        scanf("%d",&s);
        int start=n,end=1;
        while(s--)
        {
            int t;
            scanf("%d",&t);
            start=min(start,t);
            end=max(end,t);
            st[t]=1;
        }
        int ver = i + n;
        for(int j=start;j<=end;j++)
            if(st[j]) add(ver,j,1);
            else add(j,ver,0);
    }

    topsort();

    for(int i=1;i<=n;i++) dist[i]=1;
    for(int i=0;i<n+m;i++)
    {
        int j = q[i];
        for(int k = h[j];~k;k=ne[k])
            dist[e[k]]=max(dist[e[k]],dist[j]+w[k]);
    }

    int res = 0;
    for(int i=1;i<=n;i++) res=max(res,dist[i]);
    printf("%d\n",res);
    return 0;
}