洛谷 P1379 八数码难题

题目描述

3×3 的棋盘上,摆有八个棋子,每个棋子上标有 1 8 的某一数字。棋盘中留有一个空格,空格用 0 来表示。空格周围的棋子可以移到空格中。要求解的问题是:给出一种初始布局(初始状态)和目标布局(为了使题目简单,设目标状态为 123804765 ),找到一种最少步骤的移动方法,实现从初始布局到目标布局的转变。

输入输出格式

输入格式:

输入初始状态,一行九个数字,空格用 0 表示

输出格式:

只有一行,该行只有一个数字,表示从初始状态到目标状态需要的最少移动次数(测试数据中无特殊无法到达目标状态数据)

输入输出样例

输入样例#1:

283104765

输出样例#1:

4

solution

  • 求的是最小步数,属于最优解问题于是就可以搜索了

  • 因为知道末状态,所以可以双向bfs

  • 但我感觉那个太难写了..所以我们来写A*吧

  • 估价函数是当前状态的所有数字的位置对应他们的末状态的位置的曼哈顿距离之和

  • 也就是Guess=i=19(abs(aimx[i]nowx[i])+abs(aimy[i]nowy[i]))

    • aim 表示目标状态, now 表示当前状态, i <script type="math/tex" id="MathJax-Element-135">i</script> 表示的是数字

    • code

      #include<cstdio>
      #include<algorithm>
      using namespace std;
      const int dx[4]={0,0,1,-1},dy[4]={1,-1,0,0};
      const int aimx[10]={1,0,0,0,1,2,2,2,1};
      const int aimy[10]={1,0,1,2,2,2,1,0,0};
      int sta[3][3],Step,px,py,nowx[10],nowy[10];
      bool flag;
      int Guess() {
          int sum=0;
          for(int i=0;i<3;i++)
              for(int j=0;j<3;j++)
                  nowx[sta[i][j]]=i,nowy[sta[i][j]]=j;
          for(int i=1;i<9;i++)
                  sum+=(abs(aimx[i]-nowx[i])+abs(aimy[i]-nowy[i]));
          return sum;
      }
      void Astar(int nowstep,int Step,int px,int py) {
          if(flag||nowstep>Step) return;
          int H=Guess();
          if(H==0) {flag=true;return;}
          if(H+nowstep>Step) return;
          for(int i=0;i<4;i++) {
              int x=px+dx[i],y=py+dy[i];
              if(x<0||y<0||x>2||y>2) continue;
              swap(sta[x][y],sta[px][py]);
              Astar(nowstep+1,Step,x,y);
              swap(sta[x][y],sta[px][py]);
          }
          return;
      }
      int main() {
          for(int i=0;i<3;i++)
              for(int j=0;j<3;j++) {
                  char c;
                  scanf("%c",&c);
                  sta[i][j]=c-'0';
                  if(sta[i][j]==0) px=i,py=j;
              }
          while(!flag) Step++,Astar(0,Step,px,py);
          printf("%d",Step);
          return 0;
      }
      
### 关于动态规划 (Dynamic Programming, DP) 的解决方案 在解决洛谷平台上的编程问题时,尤其是涉及动态规划的题目,可以采用以下方法来构建解决方案: #### 动态规划的核心思想 动态规划是一种通过把原问题分解为相对简单的子问题的方式来求解复杂问题的方法。其核心在于存储重复计算的结果以减少冗余运算。通常情况下,动态规划适用于具有重叠子问题和最优子结构性质的问题。 对于动态规划问题,常见的思路包括定义状态、转移方程以及边界条件的设计[^1]。 --- #### 题目分析与实现案例 ##### **P1421 小玉买文具** 此题是一个典型的简单模拟问题,可以通过循环结构轻松完成。以下是该问题的一个可能实现方式: ```cpp #include <iostream> using namespace std; int main() { int n; cin >> n; // 输入购买数量n double p, m, c; cin >> p >> m >> c; // 输入单价p,总金额m,优惠券c // 计算总价并判断是否满足条件 if ((double)n * p <= m && (double)(n - 1) * p >= c) { cout << "Yes"; } else { cout << "No"; } return 0; } ``` 上述代码实现了基本逻辑:先读取输入数据,再根据给定约束条件进行验证,并输出最终结果[^2]。 --- ##### **UOJ104 序列分割** 这是一道经典的区间动态规划问题。我们需要设计一个二维数组 `f[i][j]` 表示前 i 次操作后得到的最大价值,其中 j 是最后一次切割的位置。具体实现如下所示: ```cpp #include <bits/stdc++.h> using namespace std; const int MAXN = 5e3 + 5; long long f[MAXN], sumv[MAXN]; int a[MAXN]; int main(){ ios::sync_with_stdio(false); cin.tie(0); int n,k; cin>>n>>k; for(int i=1;i<=n;i++)cin>>a[i]; for(int i=1;i<=n;i++)sumv[i]=sumv[i-1]+a[i]; memset(f,-0x3f,sizeof(f)); f[0]=0; for(int t=1;t<=k;t++){ vector<long long> g(n+1,LLONG_MIN); for(int l=t;l<=n;l++)g[l]=max(g[l-1],f[t-1][l-1]); for(int r=t;r<=n;r++)f[r]=max(f[r],g[r]+sumv[r]*t); } cout<<f[n]<<&#39;\n&#39;; return 0; } ``` 这段程序利用了滚动数组优化空间复杂度,同时保持时间效率不变[^3]。 --- ##### **其他常见问题** 针对更复杂的路径覆盖类问题(如 PXXXX),我们往往需要结合一维或多维动态规划模型加以处理。例如,在某些场景下,我们可以设定 dp 数组记录到达某一点所需最小代价或者最大收益等指标[^4]。 --- ### 总结 以上展示了如何运用动态规划技巧去应对不同类型的算法挑战。无论是基础还是高级应用场合,合理选取合适的数据结构配合清晰的状态转换关系都是成功解决问题的关键所在。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值