其他分享
首页 > 其他分享> > 【题解】Luogu P2730

【题解】Luogu P2730

作者:互联网

蒟蒻的第一道蓝题……好像也没有蓝的程度

一篇无STL的超弱题解(入门写法无误了QAQ

传送门

很经典的一道BFS

这是初始状态。

这是初始状态

操作A

qwq侵删

操作B

qwq侵删

操作C

qwq侵删

思路1 不使用cantor展开的情况

1. 对于存储这个操作序列

2.进行扩展

3.输出

请从变量定义及主函数开始阅读

#include <cstdio>
#include <cmath>//pow函数的头文件

int tl[100000][3],maxn=100000;//tl是我们存储状态的队列。tl[i][1]是指第i个情况的魔板状态,tl[i][0]是第i个情况的父亲位置,t[i][2]是指第i个情况的父亲的操作(能得到i情况)

int now=12345678,finish=0;//初始状态&目标状态

int way[100000];//用于最后逆推过程的时候存储父亲位置

bool book[87654322]={};//判重(真的不会M可能是因为最开始存int

//下面三个操作函数都很乱,建议手算公式<qwq>

void A(int j)//A操作!
{
    int qwq=0;
    for (int i=8;i>=1;i--)
    {
        qwq+=((tl[j][1]%10)*pow(10,i-1));
        tl[j][1]/=10;
    }
    tl[j][1]=qwq;
    return;
}


void B(int j)//B操作!
{
    tl[j][1]=(tl[j][1]%100000/10000)*10000000+(tl[j][1]/100000)*10000+tl[j][1]%1000*10+tl[j][1]%10000/1000;
}


void C(int j)//C操作!
{
    tl[j][1]=tl[j][1]-((tl[j][1]/100000)%100)*100000-(tl[j][1]%1000)/10*10+tl[j][1]/1000000%10*100000+tl[j][1]/100000%10*100+tl[j][1]/100%10*10+tl[j][1]/10%10*1000000;//2367->7236
}

///////////////////////////////////分割线正常阅读


bool look_for(int n)//判重,即新情况的模版状态有没有在队列中出现过
{
    return book[ tl[n][1] ];
}



bool found(int w)//判断是否达到目标状态
{
    if ( tl[w][1] == finish )
    {
        return true;
    }
    return false;
}


void print(int zt)//打印函数。zt是指队列第zt种情况
{
    if ( zt == 1 )//请联系start函数的第一句“if……”理解
    {
        printf("0");
        return;
    }
    int k=0,z=zt;
    while (z>0)//逆推!比较难理解可以试着手画一下示意
    {
        k++;
        
        way[k] = z;
        z = tl[z][0];
    }
    printf("%d\n",k-1);
    for (int i=k-1;i>0;i--)
    {
        z = way[i];
        
        printf("%c",tl[z][2]+'A'-1);//转化为字符输出!原本是用123表示ABC操作
    }
    return;
}
void start()//开始啦!
{
    if ( found(1) )//如果初始状态==目标状态
    {
        print(1);
        
        return;
    }
    int i=1,j=2;
    while ( i<j && j<maxn )//仍有状态可以扩展&未溢出上限
    {
        for (int k=1;k<=3;k++)
        {
        
            tl[j][1]=tl[i][1];//预入队!
            
            
            if (k==1){  A(j);  }//A操作
            
            if (k==2){  B(j);  }//B操作
            
            if (k==3){  C(j);  }//C操作
            
            
            if ( ! look_for(j) )//如果这种状态在之前没有出现过
            {
            
                tl[j][0]=i;//tl[j][0]:我父亲叫i!
                
                tl[j][2]=k;//tl[j][2]:我是i和k的孩子!
                
                
                if (found(j))//如果已经达到了目标状态
                {
                    print(j);//输出!
                    return;
                }
                else {  book[ tl[j][1] ]=1;  j++;  }//标记这种状态已经出现过&队尾+1
            }
        }
        i++;//一种情况的扩展完毕,队首+1
    }
}
int main()
{
    for (int i=8;i>=1;i--)//输入,转换成int
    {
        int x;
        scanf("%d",&x);
        finish+=(x*pow(10,i-1));
    }
    
    tl[1][1]=now; tl[1][0]=0;//队列的第一个状态就是初始状态now,ta的父亲没有了……
    
    start();//开始吧!
    
    return 0;//圆满的结束……
}

思路2 cantor展开

什么是康托展开?

这里提供一个正向康托展开的代码。 具体可以戳这里

int factorial[11]={1,1,2,6,24,120,720,5040,40320,362880};//0-10的阶乘!


int cantor(int a[]){//正向康托展开,求排列a[]在1~8的全排列中占第几小

    int ans=0,sum=0;
    for(int i=1;i<=7;i++)//最后一位不用计算(0*0!) 
    {
        for(int j=i+1;j<=8;j++)//寻找这个排列的第i位的后面有多少个数小于ta 
        {
        
            if(a[j]<a[i]) sum++;
            
        }
        
        ans+=sum*factorial[8-i];//ans+=sum*(8-1)! 
        
        sum=0;//计数归零 
    }
    
    
    return ans+1;//这里计算的ans是比a[]排列小的排列的个数,所以a[]排列是第ans+1小的! 
}

其余的思路基本是相同的。不过对于ABC三种操作请使用swap来实现。

蒟蒻的第一篇题解qwq
准备被残忍拒绝

标签:10,存储,魔板,int,题解,tl,P2730,Luogu,状态
来源: https://www.cnblogs.com/Kan-kiz/p/10625565.html