zoukankan      html  css  js  c++  java
  • HNOI2006 潘多拉的盒子

    题目描述

    题解:

    题目的描述比较长,理解起来也有一定难度。仔细读题后我们发现整个任务可以分成两个部分:找出咒语机之间所有的升级关系、求最长升级序列。

    1、 求升级关系:

    容易看出,咒语机i可以抽象成一个图Gi,其顶点集Vini个元件,每个顶点发出两条边——“0”边和“1”边,分别表示将信号加“0”和加“1”。

    我们枚举两个咒语机AB(AB),判断B是否是A的升级。最简单的想法是生成出AB的所有咒语源,然后判断前者是否为后者的子集。但是,一个咒语机产生的咒语源可能有无限多个,无法逐一判断。

    其实,只要存在一个咒语源,A能够产生而B不能产生,那么这一升级关系就不成立。为了找到(或者证明不存在)这样的一个咒语源,我们构造图H,其顶点是一个二元组(i,j),表示图GA的顶点i和图GB的顶点j。如果图GA的顶点i“c”边到达顶点ic(c=0,1),图GB的顶点j“c”边到达顶点jc,那么从图H的顶点(i,j)连有向边到(ic,jc)。我们将图H中的某些顶点(i,j)称为“关键顶点”,其特点是:图GA的顶点i是输出元而图GB的顶点j不是输出元。存在一个A能够产生而B不能的咒语源,等价于图H中存在从顶点(0,0)到关键顶点的路径。我们只需用广度优先搜索遍历图H即可。

    2、 求最长升级序列:

    假设第1部分求出的升级关系保存在图G中:如果咒语机BA的升级,那么图G中从AB连一条有向边。最长升级序列在图中对应最长路经。如果G是有向无环图,那么可以用拓扑排序加上动态规划的方法求最长路径。可惜的是,G有可能存在环,并且只有一种可能:存在若干个咒语机,它们产生的咒语源完全相同。显然,在这种情况下,只要选择其中一个,那么与之相同的所有咒语机都可以被选择。因此,我们把图中所有相同的咒语机合并成一个结点,并用num域记录该结点是由多少个结点合并而来(如图1)。

    这样,问题转化为在一个有向无环图中求带权最长路经,这同样可以用拓扑排序加上动态规划的方法解决。具体的方法是:首先将图的顶点重新编号使得1,2,…,n是图的拓扑序,然后利用状态转移方程求解即可。

    以上两个部分中,第一部分的时间复杂度为O(n2s2),第二部分的时间复杂度为O(s2),所以算法的总时间复杂度为O(n2s2)

    代码:

    #include<queue>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    const int N = 55;
    int s;
    struct node
    {
        int n,m;
        bool ot[N];
        int ch[N][2];
        void read()
        {
            scanf("%d%d",&n,&m);
            for(int a,i=1;i<=m;i++)
            {
                scanf("%d",&a);
                ot[a]=1;
            }
            for(int i=0;i<n;i++)
            {
                scanf("%d%d",&ch[i][0],&ch[i][1]);
            }
        }
    }p[N];
    int hed[N],cnt;
    struct EG
    {
        int fr,to,nxt;
    }e[N*N],e0[N*N];
    void ae(int f,int t)
    {
        e[++cnt].fr = f;
        e[cnt].to = t;
        e[cnt].nxt = hed[f];
        hed[f] = cnt;
    }
    struct Pair
    {
        int x,y;
        Pair(){}
        Pair(int x,int y):x(x),y(y){}
    };
    bool vs[N][N];
    bool check(node &a,node &b)
    {
        memset(vs,0,sizeof(vs));
        queue<Pair>q;
        q.push(Pair(a.ch[0][0],b.ch[0][0]));
        q.push(Pair(a.ch[0][1],b.ch[0][1]));
        while(!q.empty())
        {
            Pair u = q.front();
            q.pop();
            if(!a.ot[u.x]&&b.ot[u.y])return 0;
            if(vs[u.x][u.y])continue;
            vs[u.x][u.y]=1;
            q.push(Pair(a.ch[u.x][0],b.ch[u.y][0]));
            q.push(Pair(a.ch[u.x][1],b.ch[u.y][1]));
        }
        return 1;
    }
    int dep[N],low[N],tot;
    bool vis[N];
    int bel[N],bc,siz[N],sta[N],tl;
    void tarjan(int u)
    {
        dep[u]=low[u]=++tot;
        vis[u]=1;
        sta[++tl] = u;
        for(int j=hed[u];j;j=e[j].nxt)
        {
            int to = e[j].to;
            if(!dep[to])
            {
                tarjan(to);
                low[u] = min(low[u],low[to]);
            }else if(vis[to])
            {
                low[u] = min(low[u],dep[to]);
            }
        }
        if(dep[u]==low[u])
        {
            bc++;
            int c = -1;
            while(c!=u)
            {
                c = sta[tl--];
                bel[c]=bc;
                siz[bc]++;
                vis[c] =0;
            }
        }
    }
    bool eg[N][N];
    int Hed[N],Cnt;
    void AE(int f,int t)
    {
        e0[++Cnt].to = t;
        e0[Cnt].nxt = Hed[f];
        Hed[f] = Cnt;
    }
    int dp[N];
    int dfs(int u)
    {
        if(dp[u])return dp[u];
        if(!Hed[u])return dp[u]=siz[u];
        for(int j=Hed[u];j;j=e0[j].nxt)
            dp[u]=max(dp[u],dfs(e0[j].to)+siz[u]);
        return dp[u];
    }
    int main()
    {
    //  freopen("pandora.in","r",stdin);
    //  freopen("pandora.out","w",stdout);
        scanf("%d",&s);
        for(int i=1;i<=s;i++)p[i].read();
        for(int i=1;i<=s;i++)
            for(int j=1;j<=s;j++)
                if(i!=j&&check(p[i],p[j]))
                        ae(i,j);
        for(int i=1;i<=s;i++)
            if(!dep[i])tarjan(i);
        for(int j=1;j<=cnt;j++)
        {
            int f = bel[e[j].fr],t = bel[e[j].to];
            if(f!=t&&!eg[f][t])eg[f][t]=1,AE(f,t);
        }
        int ans = 0;
        for(int i=1;i<=bc;i++)ans=max(ans,dfs(i));
        printf("%d
    ",ans);
        return 0;
    }
  • 相关阅读:
    lab anycast rp
    激光 & 激光器
    管道机器人结构设计及控制系统搭建
    自动开关灯装置
    基于MATLAB步态算法仿真的六足仿生机器人
    蓝牙AT模式
    语音识别LD3320
    蓝牙模块设置
    6红外遥控程序
    62、如何消除碎片文件
  • 原文地址:https://www.cnblogs.com/LiGuanlin1124/p/10304762.html
Copyright © 2011-2022 走看看