码迷,mamicode.com
首页 > 其他好文 > 详细

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

时间:2018-12-22 15:02:04      阅读:158      评论:0      收藏:0      [点我收藏+]

标签:code   scanf   alt   有用   int   getchar   tchar   cst   back   

技术分享图片

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 元旦老人与汉诺塔(记忆化搜索)

标签:code   scanf   alt   有用   int   getchar   tchar   cst   back   

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

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!