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;
}
posted @ 2023-02-28 21:58  Vijurria  阅读(43)  评论(0编辑  收藏  举报