Description
给出以第 i i 个点为祖先节点的点集,问是否存在个点的树满足条件
Input
第一行一整数 n n 表示点数,之后行每行首先输入一整数 ci c i 表示以第 i i 个点为祖先节点的点有个,然后输入这 ci c i 个点的编号 (1≤n≤1000,0≤ci≤n) ( 1 ≤ n ≤ 1000 , 0 ≤ c i ≤ n )
Output
如果存在合法方案则输出 YES Y E S 及每个点的父亲节点,根节点的父亲节点视为 0 0 ,否则输出
Sample Input
5
4 2 3 4 5
3 3 4 5
2 4 5
1 5
0
Sample Output
YES
1 2
2 3
3 4
4 5
Solution
以所给关系建有向图,如果存在合法方案那么首先该有向图应有拓扑序,且拓扑序最高的点只能有一个,在求拓扑序的过程中,对于点 v v ,会有很多个前继点也就是祖先结点会被删掉进而减小 v v 的入度,显然这个前继点必然是的最高级祖先依次到 v v 的父亲节点,故在求拓扑序的过程中不断更新的祖先节点,且新的祖先节点必然要以 v v 之前的祖先节点为祖先节点,否则不合法,如果得不到拓扑序显然无解,如果得到了拓扑序那么同时我们也得到了每个点的父亲节点,但是这并不能保证一定有解,因为以点为祖先节点的点必须以 fa[i] f a [ i ] 为祖先节点,如果这个条件也满足则有解
Code
#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
typedef pair<int,int>P;
const int INF=0x3f3f3f3f,maxn=1005;
int n,m[maxn][maxn],fa[maxn],du[maxn],vis[maxn],root;
vector<int>g[maxn];
queue<int>que;
bool Solve()
{
for(int i=1;i<=n;i++)
if(!du[i])que.push(i);
if(que.size()!=1)return 0;
int res=1;
root=que.front();
while(!que.empty())
{
int u=que.front();
que.pop();
for(int i=0;i<g[u].size();i++)
{
int v=g[u][i];
du[v]--;
if(fa[v]!=-1&&!m[fa[v]][u])return 0;
fa[v]=u;
if(!du[v])res++,que.push(v);
}
}
if(res!=n)return 0;
for(int i=1;i<=n;i++)
if(i!=root)
{
for(int j=0;j<g[i].size();j++)
if(!m[fa[i]][g[i][j]])return 0;
}
return 1;
}
int main()
{
scanf("%d",&n);
memset(du,0,sizeof(du));
memset(fa,-1,sizeof(fa));
memset(m,0,sizeof(m));
for(int i=1;i<=n;i++)
{
g[i].clear();
int num,temp;
scanf("%d",&num);
while(num--)
{
scanf("%d",&temp);
g[i].push_back(temp);
m[i][temp]=1;
du[temp]++;
}
}
if(Solve())
{
printf("YES\n");
for(int i=1;i<=n;i++)
if(i!=root)printf("%d %d\n",fa[i],i);
}
else printf("NO\n");
return 0;
}