• [bzoj4010][HNOI2015]菜肴制作_贪心_拓扑排序


    菜肴制作 bzoj-4010 HNOI-2015

    题目大意:给定一张n个点m条边的有向图,求一个toposort,使得:(1)满足编号为1的点尽量在前;(2)满足(1)的情况下编号为2的点尽量在前,以此类推。

    注释:$1le n,mle 10^5$,$1le cases le 3$。


    想法:只需要先求字典序最大toposort,然后逆序输出即可。

    简单的证明:字典序最大的toposort是将当前所有解锁的中编号最大的点放在首位,这样1就会自然而然排在最后。自然就会满足所有的条件。

    更严格地,我们采用数学归纳法。

    奠基:如上文,我们将可能放在1前面的都放在了1前面,逆序输出的话就会使得1的位置是可能情况下最靠前的,即满足(1)。

    归纳假设:若前i个条件都会且仅会在最大拓扑序逆序的条件下被满足,那么对于第i+1点,在前i个点都满足题意的时候,当前解锁的点中如果没有i+1,不考虑。

    如果有i+1:

    当这些点中还有比i+1更小的点,由归纳假设,想满足前i个条件,只能先放比i大的点,所以比i+1小的点我们不理它们。

    那么只考虑不小于i+1的点,我们期望满足条件(i+1)。

    所以我们将所有比i+1大的点都先放出来,这样会最大限度地使i+1靠前。

    直到当前解锁的点中i+1为最大者,我们才将它放出来。

    这样的话,因为满足前i个条件的话我们不能解锁小于i+1的点。在这样的情况下我们又将所有可能放在i+1后面的点放在了i+1后面,所以i+1是在满足前i个条件下最靠前的。

    这样我们就满足了条件(i+1)。

    证毕

    最后,附上丑陋的代码... ...

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    #define N 100010 
    using namespace std;
    priority_queue<int>q;
    int to[N],nxt[N],head[N],tot;
    bool vis[N]; int num[N],cnt,ans[N],n,m;
    inline void add(int x,int y)
    {
        to[++tot]=y;
        nxt[tot]=head[x];
        head[x]=tot;
    }
    inline void original()
    {
        tot=cnt=0;
        memset(head,0,sizeof head);
        memset(num,0,sizeof num);
        memset(vis,false,sizeof vis);
        while(!q.empty()) q.pop();
    }
    bool flag;
    void toposort()
    {
        while(!q.empty())
        {
            int x=q.top(); q.pop();
            ans[++cnt]=x;
            vis[x]=true;
            for(int i=head[x];i;i=nxt[i])
            {
                num[to[i]]--;
                if(!num[to[i]]) q.push(to[i]);
            }
        }
        for(int i=1;i<=n;i++) if(!vis[i]) {flag=false; return; }
    }
    int main()
    {
        int cases; cin >> cases ;
        while(cases--)
        {
            original();
            cin >> n >> m ;
            for(int x,y,i=1;i<=m;i++) scanf("%d%d",&x,&y),add(y,x),num[x]++;
            flag=true;
            for(int i=1;i<=n;i++)
            {
                if(num[i]) continue;
                q.push(i);
            }
            toposort();
            if(flag) for(int i=cnt;i>=1;i--) printf("%d ",ans[i]);
            else printf("Impossible! ");
            puts("");
        }
    }
    /*
    3 
    5 4 
    5 4 
    5 3 
    4 2 
    3 2 
    3 3 
    1 2 
    2 3 
    3 1 
    5 2 
    5 2 
    4 3 
    */
    

    小结大胆假设,小心求证

  • 相关阅读:
    路径规划算法总结
    常用滤波器整理
    Debian 9 strech 安装 ROS lunar
    understand 安装笔记
    protobuf 安装与卸载
    maven-surefire-plugin
    spring数据源、数据库连接池
    日志插件总结
    pom.xml常用元素解析
    BeanFactory笔记
  • 原文地址:https://www.cnblogs.com/ShuraK/p/9397611.html
Copyright © 2020-2023  润新知