洛谷P3243【HNOI2015】菜肴制作

题目传送门

这道题看上去就知道是拓扑排序,再一看题,应该是求字典序最小的合法方案,所以贪心的找当前入度为0的编号最小的点就行了。

恭喜,你掉到坑里了。

其实我一开始就是这么做的,后来在测样例第三组数据的时候炸掉了,这组数据就可以卡掉这个贪心 (这样例还真良心啊)

我们不妨反着考虑,最后一位放合法的最大值(设为 x x x)显然是最优的,因为这样可以让小于 x x x的数更加往前,而大于 x x x的数因为 x x x的位置已经确定了所以在哪都没有影响。填完最后一位后,再考虑倒数第二位、倒数第三位……最后到第一位,我们就会发现,答案反序过来就是字典序最大的序列,所以我们建立反图,然后贪心的找字典序最大的拓扑序列,再反向输出就好了。

贴代码

#include
#include
#include
#include
using namespace std;
const int N=100010;
struct node
{
    int cnt,id;
    friend bool operator<(const node &x,const node &y)
	{ if(x.cnt==y.cnt)return x.id<y.id;return x.cnt>y.cnt;}
	//因为用了结构体所以要重载运算符 
}a[N];
int t,m,n,top,ans[N],h[N],pre[N],to[N];
bool vis[N];
priority_queue<node>q;
void ins(int u,int v)
{
    pre[++top]=h[u];h[u]=top;to[top]=v;
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d%d",&n,&m);ans[0]=top=0;bool f=true;
        memset(vis,false,sizeof(vis));memset(h,0,sizeof(h));
        while(!q.empty())q.pop();
        for(int i=1;i<=n;i++)a[i].cnt=0,a[i].id=i;//初始化一些要用的数组和优先队列 
        while(m--)
        {
            int x,y;scanf("%d%d",&x,&y);
            ins(y,x);a[x].cnt++;//反向建边,统计入度 
        }
        for(int i=1;i<=n;i++)q.push(a[i]);
        while(!q.empty())
        {
            int x=q.top().id;q.pop();
            if(vis[x])continue;
            if(a[x].cnt>0){ f=false;break;}
			//如果当前入度最小的点的入度大于0,说明无解 
            vis[x]=true;ans[++ans[0]]=x;
            for(int i=h[x];i;i=pre[i])
            {
                int v=to[i];
                a[v].cnt--;q.push(a[v]);
            }
        }
        if(!f)printf("Impossible!\n");
        else
        {
            for(int i=n;i>1;i--)printf("%d ",ans[i]);
            printf("%d\n",ans[1]);
        }//反向输出 
    }
    return 0;
}

你可能感兴趣的:(图论,拓扑排序)