uoj167 元旦老人与汉诺塔(记忆化搜索)

Posted yimmortal

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了uoj167 元旦老人与汉诺塔(记忆化搜索)相关的知识,希望对你有一定的参考价值。

技术分享图片

QwQ太懒了,题目直接复制uoj的了

QwQ这个题可以说是十分玄学的一道题了

首先可以暴搜,就是(dfs)然后模拟每个过程是哪个柱子向哪个柱子移动

不多解释了,不过实现起来还是有一点点难度的

直接上代码吧

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>

using namespace std;

inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch==‘-‘) f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-‘0‘;ch=getchar();}
  return x*f;
}

const int maxn = 110;
const int mod = 998244353;

int a[maxn][maxn];
int bel[maxn];
int top[maxn];
int st[maxn];
int ed[maxn];
int num;
int n,m;
int ans;

void to(int i,int j)
{
    int x = a[i][top[i]];
    if (ed[x]==i) num--;
    a[i][top[i]--]=0;
    a[j][++top[j]]=x;
    if (ed[x]==j) num++;
}

void dfs(int tmp)
{
    //for (int i=1;i<=3;i++)
    //{
    //  cout<<"第"<<i<<"个柱子: " ;
    //  for (int j=1;j<=top[i];j++)
    // {
    //      cout<<a[i][j]<<" ";
     // }
    //  cout<<endl;
    //}
    //cout<<"---------------------"<<endl;
    if (num==n){ans++;if (ans>mod) ans-=mod;};
    if (tmp==m+1) return;
    for (int i=1;i<=3;i++)
    {
        for (int j=1;j<=3;j++)
        {
            if (i==j) continue;
            if (top[i]<=0) continue;
            if (a[i][top[i]]>a[j][top[j]] && top[j]>0) continue;
            to(i,j);
            dfs(tmp+1);
            to(j,i);
        }
    }
}

int main()
{
  scanf("%d%d",&n,&m);
  if (m>14) {
    cout<<292996445%mod<<endl;
    return 0;
  }
  for (int i=1;i<=n;i++) st[i]=read();
  for (int i=1;i<=n;i++) ed[i]=read();
  for (int i=1;i<=n;i++) if (st[i]==ed[i]) num++;
  for (int i=n;i>=1;i--) a[st[i]][++top[st[i]]]=i;
  //cout<<num<<endl;
  dfs(1);
  cout<<ans;
  return 0;
}

经过仔(guan)细(kan)思(ti)考(jie)不难发现,这个题,有用的状态只有(3^n)种,我们可以令(f[i][j])表示当前的操作步数是(i),各个盘子的状态是(j)的合法移动方案数

然后记忆化一下!竟然过了!!!

具体的复杂度分析在这

技术分享图片

不过这个题还是有很多记得学习的地方!

1.模拟移动的过程只需要考虑柱子,而不是盘子
2.记录状态的时候可以用vector+map来实现 很方便

上代码

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<queue>
#include<map>

using namespace std;

inline int read()
{
  int x=0,f=1;char ch=getchar();
  while (!isdigit(ch)) {if (ch==‘-‘) f=-1;ch=getchar();}
  while (isdigit(ch)) {x=(x<<1)+(x<<3)+ch-‘0‘;ch=getchar();}
  return x*f;
}

const int mod = 998244353;
const int maxn = 110;

map<vector<int>,int> f[maxn],g[maxn];
int a[maxn],b[maxn];
int n,m;
vector<int> v,vv;
int ans=0;

int dfs(vector<int> x,int num)
{
   int cnt=0,top[10];
   if (num<0) return 0;
   memset(top,127/3,sizeof(top));
   if (g[num][x]) return f[num][x];
   g[num][x]=1;
   x.resize(n);
  // for (int i=n-1;i>=0;i--) cout<<x[i]<<endl<<endl; 
   for (int i=n-1;i>=0;i--) top[x[i]]=i;
   for (int i=1;i<=3;i++)
     for (int j=1;j<=3;j++)
     {
        if (i==j) continue;
        if (top[i]<top[j])
         {
            x[top[i]]=j;
            cnt=(cnt+dfs(x,num-1))%mod; 
            x[top[i]]=i;
          } 
     }
    f[num][x]=cnt;
    return f[num][x];
}
int main()
{
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;i++) a[i]=read(),v.push_back(a[i]);
  for (int i=1;i<=n;i++) b[i]=read();
  f[0][v]=1;
  g[0][v]=1;
  v.clear();
  for (int i=1;i<=n;i++) vv.push_back(b[i]);
  for (int i=0;i<=m;i++)
  {
     ans=(ans+dfs(vv,i))%mod;
  }
  cout<<ans;
  return 0;
}

以上是关于uoj167 元旦老人与汉诺塔(记忆化搜索)的主要内容,如果未能解决你的问题,请参考以下文章

UOJ#169元旦老人与数列

2020牛客寒假算法基础集训营3——I.牛牛的汉诺塔记忆化

汉诺塔(记录每种路径次数)

数据结构与算法 —— 汉诺塔问题

汉诺塔问题的详解-附代码

[Python3 练习] 005 汉诺塔1