当前位置:   article > 正文

动态规划与负数取余过程 —— NC266925 我不是大富翁_rabbitrabbit 拿到了一张环形的大富翁地图,地图被平均划分为了 nn 个地块,地块的

rabbitrabbit 拿到了一张环形的大富翁地图,地图被平均划分为了 nn 个地块,地块的

题目来源:

牛客小白月赛88

题目如下:

题目 我不是大富翁
Rabbit 拿到了一张环形的大富翁地图,地图被平均划分为了 n 个地块,地块的编号以 1 为起点,顺时针进行排布。即 1 号地块的顺时针方向依次为 2, 3, …… 号地块;1 号地块的逆时针方向依次为 n, n−1, …… 号地块(由于是环形的,所以 1 号地块与 n 号地块相邻,如下图所示)。


游戏过程如下:系统会给定一个长度为 m 的行动力序列 a_1,a_2,...,a_m​ ,在第 i (1≤i≤m) 回合,Rabbit 都需要移动 a_i 个地块,但是他可以自由选择移动的方向(换句话说,可以自由选择是向逆时针还是顺时针方向移动 a_i 个地块)。
          在游戏的开始时,Rabbit 位于 1 号地块,他想知道是否存在这样一种移动方式,使得 m 个回合后他依旧在 1 号地块。

输入
每个测试文件仅有一组测试数据
第一行输入两个整数 n 和 m (1≤n, m≤5000) 表示地块数量和行动回合数。
第二行输入 m 个整数 a_1,a_2,...,a_m​ (0<a_1,a_2,...,a_m<2*10^5) 表示行动力序列。

输出
如果 m 个回合后 Rabbit 依旧在 111 号地块,则输出 YES ;否则,请输出 NO 。您可以以任何大小写形式输出答案,例如,yEs 、yes 和 YeS 都将被视为肯定的回答。

尝试:深度优先搜索和部分优化:

在dfs函数中,在判断下一步之前先测试目前所在的步数是否能被剩余的步数抵消。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. bool found = false;
  4. void dfs(int iptDeg,int m,int n,vector<int> steps,bool addorminus){
  5. if(found == false){
  6. int maxN = steps.size();
  7. int nextDeg = iptDeg;
  8. int left = accumulate(steps.begin()+n-1,steps.end(),0);
  9. if(left == iptDeg){found = true;return;}
  10. if(left < iptDeg){return;}
  11. if(n == maxN){
  12. if(iptDeg%m==0){
  13. found = true;
  14. }
  15. return;
  16. }
  17. int operation = steps[n-1];
  18. if(addorminus == false){operation = operation*(-1);}
  19. nextDeg = nextDeg + operation;
  20. dfs(nextDeg,m,n+1,steps,true);
  21. dfs(nextDeg,m,n+1,steps,false);
  22. }
  23. }
  24. int main(){
  25. int m,n;
  26. cin >> m >> n;
  27. vector<int> steps(n);
  28. for(int i=0;i<n;i++){
  29. cin >> steps[i];
  30. }
  31. dfs(0,m,1,steps,true);
  32. if(found == true){cout << "YES" << endl;}
  33. else cout << "NO" << endl;
  34. return 0;
  35. }

深度优先搜索本质是一种暴力枚举做法,其时间复杂度为O(2^m),搜索算法在算法题中的作用极其有限,不应当优先考虑。考虑动态规划:如果在第m步走到了起点,那么第m-1步应该在满足距离起点为行动力序列中的a_m的位置上,故可以构建状态转移方程:

f(x,y)=f(x-1,g(y-a_x)),f(x,y)=f(x-1,g(y+a_x))

f(x)返回布尔值:true为该状态存在,false为该状态不存在。

g(x)的作用是把y-a_x转换到圆盘上合适的位置。

完整代码如下:

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int main(){
  4. int m,n;
  5. cin >> n >> m;
  6. vector<int> steps(m);
  7. for(int i=0;i<m;i++){
  8. cin >> steps[i];
  9. }
  10. vector<vector<bool>> dp(m+1,vector<bool>(n,false));
  11. dp[0][0] = true;
  12. for(int i=1;i<=m;i++){
  13. for(int j=0;j<n;j++){
  14. if (dp[i - 1][j] == true) {
  15. dp[i][(j+steps[i-1])%n] = true;
  16. dp[i][(n+((j-steps[i-1])%n))%n] = true;
  17. }
  18. }
  19. }
  20. if(dp[m][0]) cout << "YES" << endl;
  21. else cout << "NO" << endl;
  22. return 0;
  23. }

值得注意的是C++的取余操作的具体过程:

  1. if (dp[i - 1][j]) {
  2. dp[i][(j+steps[i-1])%n] = true;
  3. dp[i][(n+((j-steps[i-1])%n))%n] = true;
  4. }

一开始的代码为:

  1. if (dp[i - 1][j]) {
  2. dp[i][(j + steps[i - 1]) % n] = true;
  3. dp[i][(j - steps[i - 1]) % n] = true;
  4. }

这会导致数组越界,因为负数%正数会导致结果为负数,C++的取余操作基于整除操作,众所周知:C++的整数除法是截断取整,即:

a/b=sgn(ab)\lfloor \frac{abs(a)}{abs(b)} \rfloor

取余的操作即:

a \% b = a - b \times (a/b)

由此可知取余的结果符号和被取余的数的符号相同,同时结果的绝对值小于被取余数的绝对值。

该算法的复杂度为O(mn)

感谢你能看到这里。

参考文章:

C++ 负数取余-CSDN博客

声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号