1. 程式人生 > 實用技巧 >E. Directing Edges 解析(思維、拓樸排序)

E. Directing Edges 解析(思維、拓樸排序)

Codeforce 1385 E. Directing Edges 解析(思維、拓樸排序)

今天我們來看看CF1385E
題目連結

題目
給一張圖,其中有些邊一開始就是有向邊,有些邊一開始沒給定方向。求是否可能有一種邊的方向指定,能夠讓整張圖變成有向無環圖。

前言

完全不會......

@copyright petjelinux 版權所有
觀看更多正版原始文章請至petjelinux的blog

想法

首先先只把固定好方向的邊放到圖上,並且跑一遍拓樸排序,我是使用每次處理入度為零的點的方法,並且用queue處理下一個要看的點的方法。
注意到,這個方法處理的點的順序是(如果處理的圖是有向無環圖),會是深度為0的點先被處理完,然後才會是深度為1的點。。。
如此一來,我們紀錄每個點被處理的順序。如果發現最後總編號小於\(n\)

,代表圖中有環,所以到某步以後程式找不到入度為0的點,我們即可輸出NO。
那麼接下來我們考慮未定向的邊,觀察到,只要將邊從編號小的點連到編號大的,就一定不會有環,因為我們上面說過,編號小的深度也小。

程式碼:

const int _n=2e5+10;
int t,n,m,_,cnt,num[_n],in[_n],x,y;
bool vis[_n];
queue<int> q;
vector<PII> es,ans;
VI G[_n];
main(void) {ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
  cin>>t;while(t--){
    cin>>n>>m;rep(i,0,n+1)G[i].clear();es.clear(),ans.clear();while(!q.empty())q.pop();
    memset(num,0,sizeof(num[0])*(n+1)),cnt=0,memset(in,0,sizeof(in[0])*(n+1));
    rep(i,0,m){cin>>_>>x>>y;if(!_)es.pb({x,y});else G[x].pb(y),in[y]++,ans.pb({x,y});}
    rep(i,1,n+1)if(!in[i])q.push(i);
    while(!q.empty()){
      int now=q.front();q.pop();
      num[now]=cnt++;vis[now]=1;for(int u:G[now]){in[u]--;if(!in[u])q.push(u);}
    }if(cnt<n){cout<<"NO\n";goto A;}
    cout<<"YES\n";
    for(PII e:es)if(num[e.fi]<num[e.se])cout<<e.fi<<' '<<e.se<<'\n'; else cout<<e.se<<' '<<e.fi<<'\n';
    for(PII e:ans)cout<<e.fi<<' '<<e.se<<'\n';
    A:;
  }
  return 0;
}

標頭、模板請點Submission看
Submission