ABC 291 E - Find Permutation(图论/拓扑)
https://atcoder.jp/contests/abc291/tasks/abc291_e
题目大意:
长度为n的全排列A(A1,A2,A3,,,An)。
给定m条标记,每一条标记中有x和y,表示x这个位置上的数字会小于y这个位置上的数字,
问我们A能唯一确定吗?如果可以,随便找一个。不可以就输出No。
Sample Input 1
3 2
3 1
2 3
Sample Output 1
Yes
3 1 2
很明显是一个拓扑,但是实现细节却不好,学一下佬儿的写法。(虽然有点复杂)
方法一
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PII;
const LL MAXN=1e18,MINN=-1e18;
const LL N=2e5+10,M=4010;
const LL mod=998244353;
const double PI=3.1415926535;
#define endl '\n'
LL a[N],b[N],num[N];
vector<LL> v[N];
int main()
{
cin.tie(0); cout.tie(0); ios::sync_with_stdio(false);
int T=1;
//cin>>T;
while(T--)
{
LL n,m;
cin>>n>>m;
map<LL,LL> mp;
for(int i=1;i<=m;i++)
{
cin>>a[i]>>b[i];//ai位置的数字要小于bi位置的数字
v[a[i]].push_back(b[i]);//存储较大的位置
mp[b[i]]++;//对大数有要求,前面无所谓
}
queue<LL> q;
for(int i=1;i<=n;i++)
{
if(mp[i]==0)//位置不受限制的数字
{
q.push(i);//直接先放入队列中
num[i]=1;//标记初始值
}
}
while(q.size())//从前往后放数字
{
LL u=q.front();
q.pop();
for(int id:v[u])//看是否有大数限制
{
mp[id]--;//有的话限制次数--
if(mp[id]==0)//如果没有了的话就说明它这一列就到顶了
{
num[id]=num[u]+1;//可以直接标注数字
q.push(id);//压入栈中
}//没有的话就继续寻找
}
}
bool flag=true;
LL maxn=0;
for(int i=1;i<=n;i++)
{
maxn=max(maxn,num[i]);
}
if(maxn!=n)//看一下最大的数字是不是n,是的话就说明可以满足条件
{//不是的话就不行
flag=false;
cout<<"No"<<endl;
}
if(flag==true)
{
cout<<"Yes"<<endl;
for(int i=1;i<=n;i++)//是的话就直接输出每个位置上的数字
cout<<num[i]<<" ";
cout<<endl;
}
}
return 0;
}
方法二
这个方法更好理解(推荐记忆
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef pair<LL,LL> PII;
const LL MAXN=1e18,MINN=-1e18;
const LL N=2e5+10,M=4010;
const LL mod=998244353;
const double PI=3.1415926535;
#define endl '\n'
LL x,y,num[N],cnt=0;
vector<LL> v[N];
int main()
{
cin.tie(0); cout.tie(0); ios::sync_with_stdio(false);
int T=1;
//cin>>T;
while(T--)
{
LL n,m;
cin>>n>>m;
map<LL,LL> mp;
for(int i=1;i<=m;i++)
{
cin>>x>>y;
v[x].push_back(y);//先建图
mp[y]++;
}
queue<LL> q;
for(int i=1;i<=n;i++)
{
if(mp[i]==0) q.push(i);//把不受限制的数字压入栈底,标注为1(注意:为链条所以只能一开始压入唯一一个)
}
bool flag=true;
if(q.size()!=1) flag=false;
if(flag==false)
{
cout<<"No"<<endl;
}
else
{
while(q.size())
{
LL op=q.front();
q.pop();
LL ans=0;
num[op]=++cnt;//标记当前位置上的数字
for(auto id:v[op])//从当前op的位置往后深搜更大的
{
mp[id]--;//限制--
if(mp[id]==0)
{
q.push(id);//到顶了说明就是可以精确位置了,直接放下
ans++;//如果同一个点上压了两个更高的位置,说明存在不确定情况
}
}
if(ans>1)
{
flag=false;
break;
}
}
if(cnt!=n) flag=false;//没有压完到全排列的数字时就是错的
if(flag==false) cout<<"No"<<endl;
else
{
cout<<"Yes"<<endl;
for(int i=1;i<=n;i++)//对的就直接输出
{
cout<<num[i]<<" ";
}
cout<<endl;
}
}
}
return 0;
}