题解 P2321 【[HNOI2006]潘多拉的宝盒】

本文详细解析了一种算法,用于解决寻找最长升级序列的问题。通过理解题意,介绍了使用Tarjan+DFS等方法的背景,并提供了一个简化版的实现思路。关键步骤包括记录输出元、元件状态、咒语机元件指向,以及利用BFS和Floyd算法进行升级序列的判断与求解。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

做这道题的最关键的一步也是第一步:读懂题!!!!!!!

题目大意:

有s个咒语机,每个咒语机出度为2,字符串后加0指向一个元件,加1指向一个元件,直到找到一个输出元,算是一种方案;当A咒语机的所有方案包含B咒语机的所有方案时,那么A咒语机是B咒语机的升级。求:最长升级序列的长度。(我读了40分钟才读懂,语文不好)

做法:

我一开始是没有思路的,感觉像搜索,看了网上很多代码都是Tarjan+DFS+……(本人很懒,不想学这种方法),最后找到一个dalao的code,我把他的思路介绍给大家:

1.pri[i][x]记录的是输出元;

2.vis[i][j]记录的是s1中的i元件和s2中的j元件这个状态;

3.map[i][j][(0,1)]记录的是i咒语机中j元件到哪个元件;(因为n很小,所以开矩阵就好)

4.一个BFS来判断j是否是i的升级,用levup[i][j]记录;

5.Floyd找出最长升级序列。

代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
const int N=55;
int map[N][N][2],n,m,levup[N][N],ans=0;
bool vis[N][N],pri[N][N];
struct node
{
    int sx,sy;
};
bool check(int s1,int s2)//判断s2是否是s1的升级 
{
    memset(vis,0,sizeof(vis));
    queue<node>q;
    node fir;
    fir.sx=0,fir.sy=0;
    q.push(fir);
    while(!q.empty())
    {
        node x=q.front(),tmp;q.pop();
        if(pri[s1][x.sx]&&!pri[s2][x.sy])return 0;//如果 pri[s1][x.sx]是输出元,且pri[s2][x.sy]不是的话,那么s2不是s1的升级 
        tmp.sx=map[s1][x.sx][0],tmp.sy=map[s2][x.sy][0];
        if(!vis[tmp.sx][tmp.sy])vis[tmp.sx][tmp.sy]=true,q.push(tmp);
        tmp.sx=map[s1][x.sx][1],tmp.sy=map[s2][x.sy][1];
        if(!vis[tmp.sx][tmp.sy])vis[tmp.sx][tmp.sy]=true,q.push(tmp);
    }
    return 1;
}
int main()
{
    int s;
    scanf("%d",&s);
    for(int i=0;i<s;i++)//因为是从0号元件开始的所以i=0;i<s;i++ 
    {
        scanf("%d%d",&n,&m);
        for(int j=0;j<m;j++)
        {
            int x;
            scanf("%d",&x);pri[i][x]=1;
        }
        for(int j=0;j<n;j++)
        {
            int u,v;
            scanf("%d%d",&u,&v);
            map[i][j][0]=u,map[i][j][1]=v;
        }
    }
    memset(levup,-0x3f,sizeof(levup));
    for(int i=0;i<s;i++)
    {
        for(int j=0;j<s;j++)
        {
            if(check(i,j)&&i!=j&&levup[j][i]<0)//这里是levup[j][i]!!!!!!!!!!!  j  !!!!!!  i  !!!!!!!!
            //因为要i不是j的升级,避免重复,否者ans很大,最开始我写的levup[i][j]全WA 
            levup[i][j]=1;//这里是levup[i][j]!!!!!!!!!!!  i  !!!!!!  j  !!!!!!!! 
        }
    }
    for(int k=0;k<s;k++)//floyd(一个精巧的DP) 
    {
        for(int i=0;i<s;i++)
        {
            for(int j=0;j<s;j++)
            {
                if(levup[i][j]<levup[i][k]+levup[k][j]&&levup[i][k]&&levup[k][j])
                levup[i][j]=levup[i][k]+levup[k][j],ans=max(ans,levup[i][j]);
            }
        }
    }
    printf("%d",ans+1);//因为要把开头的咒语机算进去,所以ans+1 
}
欢迎dalao指正!!!!!!!!!!
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

SNiFe_Blog

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值