背景:本題為構造DAG題,給出了有向與無向邊,CF2000分題目
思路:先處理有向圖,判斷是否有環,有就NO,否則一定有解.
我們思考一下有環的條件(或者說環在什麼情況下產生):即後面的數指向前面的數才可能構成環,即拓撲序大的指回去了!
故得構造思路:即讓無向邊的拓撲序小的指向大的即不會產生環
細節:想想全為無向邊的情況下處理
程式碼如下:
點選檢視程式碼
//背景:本題為構造DAG題,給出了有向與無向邊,CF2000分題目
/*思路:先處理有向圖,判斷是否有環,有就NO,否則一定有解.
我們思考一下有環的條件(或者說環在什麼情況下產生):即後面的數指向前面的數才可能構成環,即拓撲序大的指回去了!
故得構造思路:即讓無向邊的拓撲序小的指向大的即不會產生環*/
//細節:想想全為無向邊的情況下處理
#include <bits/stdc++.h>
using namespace std;
void Solve()
{
int n,m,t,x,y;
cin>>n>>m;
vector<int> e1[n+1];//鄰接表存有向圖
vector<pair<int,int>>e2;//存無向邊
int topos[n+1];//記錄拓撲序
int in[n+1];//記錄入度
memset(topos,0,sizeof(topos));//初始拓撲序為0
memset(in,0,sizeof(in));//初始化入度為0
for (int i = 1;i<=m;i++)//輸入邊
{
cin>>t>>x>>y;
if(t == 0)
{
e2.push_back({x,y});//無向
}
else
{
in[y]++;
e1[x].push_back(y);//有向
}
}
queue<int> que;//拓撲佇列
for (int i = 1;i<=n;i++)//這裡跟以前的不太一樣!!!採用了遍歷而非拓撲圖中存在的點,即也處理不在拓撲圖中的點,其入度必為0
{
if(in[i] == 0) que.push(i);
}
int ans = 0;//初始化計數
while(!que.empty())//拓撲排序(顯然優先處理入度為0的,所以不在拓撲圖中的點也會被計算且被賦予拓撲序,神來之筆),而不在拓撲圖中的點的拓撲序必然小!!
{
x = que.front();
que.pop();
ans++;
topos[x] = ans;//記錄當前點的拓撲序
for (auto v:e1[x])
{
in[v]--;
if(in[v] == 0) que.push(v);
}
}
if(ans < n)//以n為判斷環的標準了!!!
{
cout<<"NO"<<"\n";
return;
}
cout<<"YES"<<"\n";
for (int i = 1;i<=n;i++)//先輸出有向邊
{
for (auto v:e1[i]) cout<<i<<" "<<v<<"\n";
}
for (auto v:e2)//處理無向邊,由小向大輸出
{
if(topos[v.first] < topos[v.second]) cout<<v.first<<" "<<v.second<<"\n";
else cout<<v.second<<" "<<v.first<<"\n";
}
}
int main()
{
ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);
int t;
cin>>t;
while(t--)
{
Solve();
}
return 0;
}
/*我的Hack資料:
1
4 4
0 1 2
0 2 3
0 3 4
0 4 1
*/