赞
踩
题目来源:
题目如下:
题目 我不是大富翁
Rabbit 拿到了一张环形的大富翁地图,地图被平均划分为了 n 个地块,地块的编号以 1 为起点,顺时针进行排布。即 1 号地块的顺时针方向依次为 2, 3, …… 号地块;1 号地块的逆时针方向依次为 n, n−1, …… 号地块(由于是环形的,所以 1 号地块与 n 号地块相邻,如下图所示)。
游戏过程如下:系统会给定一个长度为 m 的行动力序列 ,在第 i (1≤i≤m) 回合,Rabbit 都需要移动
个地块,但是他可以自由选择移动的方向(换句话说,可以自由选择是向逆时针还是顺时针方向移动
个地块)。
在游戏的开始时,Rabbit 位于 1 号地块,他想知道是否存在这样一种移动方式,使得 m 个回合后他依旧在 1 号地块。
输入
每个测试文件仅有一组测试数据。
第一行输入两个整数 n 和 m (1≤n, m≤5000) 表示地块数量和行动回合数。
第二行输入 m 个整数 (
) 表示行动力序列。
输出
如果 m 个回合后 Rabbit 依旧在 111 号地块,则输出 YES ;否则,请输出 NO 。您可以以任何大小写形式输出答案,例如,yEs 、yes 和 YeS 都将被视为肯定的回答。
尝试:深度优先搜索和部分优化:
在dfs函数中,在判断下一步之前先测试目前所在的步数是否能被剩余的步数抵消。
- #include<bits/stdc++.h>
- using namespace std;
- bool found = false;
-
- void dfs(int iptDeg,int m,int n,vector<int> steps,bool addorminus){
- if(found == false){
- int maxN = steps.size();
- int nextDeg = iptDeg;
- int left = accumulate(steps.begin()+n-1,steps.end(),0);
- if(left == iptDeg){found = true;return;}
- if(left < iptDeg){return;}
- if(n == maxN){
- if(iptDeg%m==0){
- found = true;
- }
- return;
- }
- int operation = steps[n-1];
- if(addorminus == false){operation = operation*(-1);}
- nextDeg = nextDeg + operation;
- dfs(nextDeg,m,n+1,steps,true);
- dfs(nextDeg,m,n+1,steps,false);
- }
- }
-
-
- int main(){
- int m,n;
- cin >> m >> n;
- vector<int> steps(n);
- for(int i=0;i<n;i++){
- cin >> steps[i];
- }
- dfs(0,m,1,steps,true);
- if(found == true){cout << "YES" << endl;}
- else cout << "NO" << endl;
- return 0;
- }

深度优先搜索本质是一种暴力枚举做法,其时间复杂度为,搜索算法在算法题中的作用极其有限,不应当优先考虑。考虑动态规划:如果在第m步走到了起点,那么第m-1步应该在满足距离起点为行动力序列中的
的位置上,故可以构建状态转移方程:
f(x)返回布尔值:true为该状态存在,false为该状态不存在。
g(x)的作用是把转换到圆盘上合适的位置。
完整代码如下:
- #include<bits/stdc++.h>
- using namespace std;
-
-
- int main(){
- int m,n;
- cin >> n >> m;
- vector<int> steps(m);
- for(int i=0;i<m;i++){
- cin >> steps[i];
- }
- vector<vector<bool>> dp(m+1,vector<bool>(n,false));
- dp[0][0] = true;
- for(int i=1;i<=m;i++){
- for(int j=0;j<n;j++){
- if (dp[i - 1][j] == true) {
- dp[i][(j+steps[i-1])%n] = true;
- dp[i][(n+((j-steps[i-1])%n))%n] = true;
- }
- }
- }
- if(dp[m][0]) cout << "YES" << endl;
- else cout << "NO" << endl;
- return 0;
- }

值得注意的是C++的取余操作的具体过程:
- if (dp[i - 1][j]) {
- dp[i][(j+steps[i-1])%n] = true;
- dp[i][(n+((j-steps[i-1])%n))%n] = true;
- }
一开始的代码为:
- if (dp[i - 1][j]) {
- dp[i][(j + steps[i - 1]) % n] = true;
- dp[i][(j - steps[i - 1]) % n] = true;
- }
这会导致数组越界,因为负数%正数会导致结果为负数,C++的取余操作基于整除操作,众所周知:C++的整数除法是截断取整,即:
取余的操作即:
由此可知取余的结果符号和被取余的数的符号相同,同时结果的绝对值小于被取余数的绝对值。
该算法的复杂度为。
感谢你能看到这里。
参考文章:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。