1. 程式人生 > >【洛谷1983】車站分級(暴力水過,正解:虛擬點優化)

【洛谷1983】車站分級(暴力水過,正解:虛擬點優化)

點此看題面

大致題意: 一條單向鐵路上有n個火車站,每個火車站有一個等級,火車若在x點停靠,則起點站與終點站之間每個等級大於等於x的等級的車站都必須停靠,現已知m趟車次的執行情況,請你求出這n個火車站至少劃分為幾個等級。

第一種方法:暴力建邊

對於每一個資訊,可以將起點站與終點站之間未出現的站與出現了的站之間連一條有向邊,最後拓撲掃一遍即可求出答案。

程式碼如下:

#include<bits/stdc++.h>
#define N 1000
#define M 1000
using namespace std;
int n,m,ee,IN[N+5],lnk[N+5],s[N+5],a[N+5]
[M+5]; bool did[N+5][N+5]={0}; struct edge { int to,nxt; }e[M*M+5]; queue<int> q; void add(int x,int y) {IN[y]++,e[++ee].to=y,e[ee].nxt=lnk[x],lnk[x]=ee;} int main() { scanf("%d%d",&n,&m); for(int i=1,j,x,y;i<=m;i++) { for(scanf("%d",&a[i][0]),j=1;j<=a[i][0];j++) scanf("%d"
,&a[i][j]); x=a[i][1]+1,y=2; while(y<=a[i][0])//暴力將未出現過的點與出現過的點連邊 { while(x<a[i][y]) {for(j=1;j<=a[i][0];j++) if(!did[x][a[i][j]]) add(x,a[i][j]),did[x][a[i][j]]=true;x++;} x++,y++; } } for(int i=1;i<=n;i++) if(!IN[i]) q.push(i),s[i]=1; while(!q.empty())//按照拓撲序掃一遍 { int
k=q.front();q.pop(); for(int i=lnk[k];i;i=e[i].nxt) { s[e[i].to]=max(s[e[i].to],s[k]+1);//更新當前節點的等級 if(!(--IN[e[i].to])) q.push(e[i].to);//若當前節點入度為0,則將其加入佇列 } } int ans=0; for(int i=1;i<=n;i++) ans=max(ans,s[i]); return printf("%d",ans),0;

第二種做法:虛擬點優化

雖然因為資料水,暴力建邊能夠ACAC,但是,此題正解當然不是這個。

正解應該是對暴力建邊加上虛擬點優化。利用虛擬點優化,可以將原先的O(n^2)建邊更改為O(n) 建邊,時間複雜度大大降低,也更方便之後的拓撲。

虛擬點優化是一個十分常用的小技巧,在做圖論題時更是發揮著重要作用,甚至可以將暴力變成滿分。
這題正解的程式碼暫時還沒寫好,等寫好了再更新。