当前位置:   article > 正文

2.8数据结构与算法学习日记(bfs和01背包和完全背包)

2.8数据结构与算法学习日记(bfs和01背包和完全背包)

P8673 [蓝桥杯 2018 国 C] 迷宫与陷阱

题目描述

小明在玩一款迷宫游戏,在游戏中他要控制自己的角色离开一间由 N×N 个格子组成的二维迷宫。

小明的起始位置在左上角,他需要到达右下角的格子才能离开迷宫。

每一步,他可以移动到上下左右相邻的格子中(前提是目标格子可以经过)。

迷宫中有些格子小明可以经过,我们用 . 表示;

有些格子是墙壁,小明不能经过,我们用 # 表示。

此外,有些格子上有陷阱,我们用 X 表示。除非小明处于无敌状态,否则不能经过。

有些格子上有无敌道具,我们用 % 表示。

当小明第一次到达该格子时,自动获得无敌状态,无敌状态会持续 K 步。

之后如果再次到达该格子不会获得无敌状态了。

处于无敌状态时,可以经过有陷阱的格子,但是不会拆除 / 毁坏陷阱,即陷阱仍会阻止没有无敌状态的角色经过。

给定迷宫,请你计算小明最少经过几步可以离开迷宫。

输入格式

第一行包含两个整数 N 和 K。(1≤N≤1000,1≤K≤10)。

以下 N 行包含一个 N×N 的矩阵。

矩阵保证左上角和右下角是 .

输出格式

一个整数表示答案。如果小明不能离开迷宫,输出 −1−1。

输入输出样例

输入 #1复制

5 3
...XX
##%#.
...#.
.###.
.....

输出 #1复制

10

输入 #2复制

5 1
...XX
##%#.
...#.
.###.
.....

输出 #2复制

12

说明/提示

时限 3 秒, 256M。蓝桥杯 2018 年第九届国赛

题目分析

1,题目需考虑无敌状态以及无敌道具,所以我们需要在bfs时把这些条件加进去考虑,定义一个标记数组用来表是否走过和当前无敌所剩时间

2.在结构体内新加一个变量 magic,表示当前还剩几步的无敌状态。在遍历过程中,如果遇到“X”,并且不剩无敌了,就不能走;如果遇到“%”,magic 会重置成k。其他情况,并且不为“#”时,该点入队。

3,再进行剪枝操作符合能走的条件(book[nx][ny]<magic的作用是把肯定不是最优的情况排除,就是当前我走这个点剩下的无敌步数要大于之前剩下的,这会更优,所以可以走)

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int N=1e3+5;
  4. int n,k;
  5. char a[N][N];
  6. int book[N][N];
  7. struct node
  8. {
  9. int x,y,step,magic;
  10. };
  11. int dx[4]= {1,-1,0,0};//方向数组
  12. int dy[4]= {0,0,1,-1};
  13. int bfs()
  14. {
  15. queue<node>p;
  16. book[1][1]=0;
  17. p.push({1,1,0,0});
  18. while(p.size())
  19. {
  20. node t=p.front();//查看队列头
  21. p.pop();
  22. if(t.x==n&&t.y==n)
  23. {
  24. cout<<t.step;
  25. return 0;//到达终点所以输出步数然后返回
  26. }
  27. for(int i=0; i<4; i++)
  28. {
  29. int nx=t.x+dx[i];
  30. int ny=t.y+dy[i];
  31. if(a[nx][ny]=='X'&&t.magic==0)//没有无敌又有陷阱,所以不能走
  32. continue;
  33. int magic=max(0,t.magic-1);//查看现在的无敌剩下步数
  34. if(a[nx][ny]=='%')magic=k;
  35. if(nx>=1&&nx<=n&&ny>=1&&ny<=n&&book[nx][ny]<magic&&a[nx][ny]!='#')//符合能走的条件(book[nx][ny]<magic的作用是把肯定不是最优的情况排除,就是当前我走这个点剩下的无敌步数要大于之前剩下的,这会更优,所以可以走)
  36. {
  37. book[nx][ny]=magic;
  38. p.push({nx,ny,t.step+1,magic});//入队更新步数和无敌状态
  39. }
  40. }
  41. }
  42. cout<<-1;
  43. return 0;
  44. }
  45. int main(){
  46. cin>>n>>k;
  47. for(int i=1;i<=n;i++)
  48. for(int j=1;j<=n;j++)
  49. cin>>a[i][j];
  50. memset(book,-1,sizeof(book));//初始化为-1
  51. bfs();
  52. }

P1115 最大子段和

题目描述

给出一个长度为 n 的序列 a,选出其中连续且非空的一段使得这段和最大。

输入格式

第一行是一个整数,表示序列的长度 n。

第二行有 n 个整数,第 i 个整数表示序列的第 i 个数字 ai​。

输出格式

输出一行一个整数表示答案。

输入输出样例

输入 #1复制

7
2 -4 3 -1 2 -4 3

输出 #1复制

4

说明/提示

样例 1 解释

选取 [3,5][3,5] 子段 {3,−1,2}{3,−1,2},其和为 44。

数据规模与约定
  • 对于 40%40% 的数据,保证 n≤2×103。
  • 对于 100%100% 的数据,保证 1≤n≤2×105,−104≤ai​≤104。

题目分析 

1,既然是求最大的子段和,所以我们要找出每个子段的和并边找边比大小,所以有个max变量一直在存放最大值

2,记录子段和,b[i]记录到i时,i之前的所有子段和

3,对于一直往后加,我们可以这样考虑(取与不取)取的条件是要求取他后总和会比之前的大,不然取着没意义,不取是加上后还比之前的小,那么就到这吧,不取了,下一个子段的开头以他为首赋值给b[i];

代码示例

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int b[200020],a[200020],i,n,ans=-9999;//因为要取最大值,所以从最小的开始找
  4. int main(){
  5. cin>>n;
  6. for(i=1;i<=n;i++)
  7. {cin>>a[i];
  8. if(n==1)b[i]=a[i];
  9. else b[i]=max(a[i],b[i-1]+a[i]);//判断取不取,哪个更优
  10. ans=max(ans,b[i]);//最大值
  11. }
  12. cout<<ans;
  13. }

 p1616疯狂的采药

题目背景

此题为纪念 LiYuxiang 而生。

题目描述

LiYuxiang 是个天资聪颖的孩子,他的梦想是成为世界上最伟大的医师。为此,他想拜附近最有威望的医师为师。医师为了判断他的资质,给他出了一个难题。医师把他带到一个到处都是草药的山洞里对他说:“孩子,这个山洞里有一些不同种类的草药,采每一种都需要一些时间,每一种也有它自身的价值。我会给你一段时间,在这段时间里,你可以采到一些草药。如果你是一个聪明的孩子,你应该可以让采到的草药的总价值最大。”

如果你是 LiYuxiang,你能完成这个任务吗?

此题和原题的不同点:

1. 每种草药可以无限制地疯狂采摘。

2. 药的种类眼花缭乱,采药时间好长好长啊!师傅等得菊花都谢了!

输入格式

输入第一行有两个整数,分别代表总共能够用来采药的时间 t 和代表山洞里的草药的数目 m。

第 2 到第 (m+1) 行,每行两个整数,第 (i+1) 行的整数 ai​,bi​ 分别表示采摘第 i 种草药的时间和该草药的价值。

输出格式

输出一行,这一行只包含一个整数,表示在规定的时间内,可以采到的草药的最大总价值。

输入输出样例

输入 #1复制

70 3
71 100
69 1
1 2

输出 #1复制

140

说明/提示

数据规模与约定
  • 对于 30%30% 的数据,保证 m≤103 。
  • 对于 100%100% 的数据,保证 1≤m≤104,1≤t≤107,且 1≤m×t≤107,1≤ai​,bi​≤104

 题目分析

1,和之前的采药http://t.csdnimg.cn/SFpwD不同的是,他是可以重复采摘,所以在这里就变成了完全背包问题

2,此时给他数组压缩成一维数组得到dp[j]=max(dp[i],dp[j-w[i]]+v[i])

3.j表截止到j时的最大容量,dp[j]表截止到j容量的价值

代码示例

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. const int maxm=10010,maxt=10000010;
  4. long long v[maxm],t[maxt],dp[maxt];
  5. int main(){
  6. int T,m;
  7. cin>>T>>m;
  8. for(int i=1;i<=m;i++)
  9. cin>>t[i]>>v[i];
  10. for(int i=1;i<=m;i++)
  11. for(int j=t[i];j<=T;j++)//完全背包一维数组用从前往后遍历(这样会保证重复取),01背包一维数组从后往前遍历
  12. dp[j]=max(dp[j],dp[j-t[i]]+v[i]);//判断是否取,然后j表截止到j的最大容量,dp[j]表截止到j容量的价值
  13. cout<<dp[T];
  14. }


    

           

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小小林熬夜学编程/article/detail/77724
推荐阅读
相关标签
  

闽ICP备14008679号