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