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;
}

原文地址:https://www.cnblogs.com/yimmortal/p/10160842.html

时间: 2024-10-08 06:04:16

uoj167 元旦老人与汉诺塔(记忆化搜索)的相关文章

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

https://ac.nowcoder.com/acm/contest/3004/I 题意:输出汉诺塔移动过程中每一种移动的次数和移动总数. 如下A->B:XXA->C:XXB->A:XXB->C:XXC->A:XXC->B:XXSUM:XX 解法:记忆化搜索,当前状态的可以由上一状态得到. #include <cstdio> #include <cstring> #include <cmath> #include <algor

hdu 1207 汉诺塔II (DP+递推)

汉诺塔II Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 4529    Accepted Submission(s): 2231 Problem Description 经典的汉诺塔问题经常作为一个递归的经典例题存在.可能有人并不知道汉诺塔问题的典故.汉诺塔来源于印度传说的一个故事,上帝创造世界时作了三根金刚石柱子,在一根柱子上从下往

从汉诺塔问题来看“递归”本质

汉诺塔问题 大二上数据结构课,老师在讲解"栈与递归的实现"时,引入了汉诺塔的问题,使用递归来解决n个盘在(x,y,z)轴上移动. 例如下面的动图(图片出自于汉诺塔算法详解之C++): 三个盘的情况: 四个盘的情况: 如果是5个.6个.7个....,该如何移动呢? 于是,老师给了一段经典的递归代码: void hanoi(int n,char x,char y,char z){ if(n == 1) move(x,1,z); else{ hanoi(n-1,x,z,y); move(x,

bzoj1019: [SHOI2008]汉诺塔(dp)

1019: [SHOI2008]汉诺塔 Time Limit: 1 Sec  Memory Limit: 162 MBSubmit: 1739  Solved: 1062[Submit][Status][Discuss] Description 汉诺塔由三根柱子(分别用A B C表示)和n个大小互不相同的空心盘子组成.一开始n个盘子都摞在柱子A上,大的在下面,小的在上面,形成了一个塔状的锥形体. 对汉诺塔的一次合法的操作是指:从一根柱子的最上层拿一个盘子放到另一根柱子的最上层,同时要保证被移动的

汉诺塔(三)

汉诺塔(三) 描述 在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针.印度教的主神梵天在创造世界的时候,在其中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔.不论白天黑夜,总有一个僧侣在按照下面的法则移动这些金片:一次只移动一片,不管在哪根针上,小片必须在大片上面.僧侣们预言,当所有的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中消灭,而梵塔.庙宇和众生也都将同归于尽. 现在我们把三根针编号为1,2,3. 所

python 实现汉诺塔问题

代码如下: def hano(n,x,y,z): if n==1: print(x,"->",z) else: #将n-1个盘子从x->y hano(n-1,x,z,y) #将剩余的最后一个盘子从x->z print(x,"->",z) #将剩余的n-1个盘子从y->z hano(n-1,y,x,z) n = int(input("请输入汉诺塔的层数:")) hano(n,"A","B&

NYOJ 93 汉诺塔(三) 【栈的简单应用】

汉诺塔(三) 时间限制:3000 ms  |  内存限制:65535 KB 难度:3 描写叙述 在印度,有这么一个古老的传说:在世界中心贝拿勒斯(在印度北部)的圣庙里,一块黄铜板上插着三根宝石针.印度教的主神梵天在创造世界的时候.在当中一根针上从下到上地穿好了由大到小的64片金片,这就是所谓的汉诺塔. 不论白天黑夜,总有一个僧侣在依照以下的法则移动这些金片:一次仅仅移动一片.无论在哪根针上.小片必须在大片上面.僧侣们预言.当全部的金片都从梵天穿好的那根针上移到另外一根针上时,世界就将在一声霹雳中

汉诺塔的递归算法与解析

从左到右 A  B  C 柱 大盘子在下, 小盘子在上, 借助B柱将所有盘子从A柱移动到C柱, 期间只有一个原则: 大盘子只能在小盘子的下面. 如果有3个盘子, 大中小号, 越小的越在上面, 从上面给盘子按顺序编号 1(小),2(中),3(大), 后面的原理解析引用这里的编号. 小时候玩过这个游戏, 基本上玩到第7个,第8个就很没有耐心玩了,并且操作的动作都几乎相同觉得无聊.  后来学习编程, 认识到递归, 用递归解决汉诺塔的算法也是我除了简单的排序算法后学习到的第一种算法. 至于递归,简单来说

BZOJ 1019: [SHOI2008]汉诺塔

Description 一个汉诺塔,给出了移动的优先顺序,问从A移到按照规则移到另一个柱子上的最少步数. 规则:小的在大的上面,每次不能移动上一次移动的,选择可行的优先级最高的. Sol DP. 倒着DP.但是他有优先级,所以他的方案是唯一的. 状态 \(f[a][i]\) 表示 将 \(a\) 柱上的 \(i\) 个移到,能移动到的柱子上的步数. 他能移动到的柱子也是唯一的,这个可以跟DP一起递推出来. \(g[a][j]\) 表示 将 \(a\) 柱上的 \(i\) 个能移动到的柱子. 然后