• 935.骑士拨号器 - 力扣


    935.骑士拨号器 - 力扣

    题目链接:935. 骑士拨号器 - 力扣(LeetCode)

    题目:

    在这里插入图片描述

    示例 1:

    输入:n = 1
    输出:10
    解释:我们需要拨一个长度为1的数字,所以把骑士放在10个单元格中的任何一个数字单元格上都能满足条件。
    

    示例 2:

    输入:n = 2
    输出:20
    解释:我们可以拨打的所有有效号码为[04, 06, 16, 18, 27, 29, 34, 38, 40, 43, 49, 60, 61, 67, 72, 76, 81, 83, 92, 94]
    

    示例 3:

    输入:n = 3131
    输出:136006598
    解释:注意取模
    

    题解:

    题目大意:给定一个电话垫,不同数字之间的移动只能是L形状的路线(L可以旋转),并且只能在数字之间移动,求出长度为n的不同电话号码有多少个?

    读完题目之后,一个很简单的思路就是模拟,使用暴力的方式来模拟这一过程,最初我是用的是深度优先搜索算法进行暴力求解,但是回出现栈溢出的情况,在输入N = 3131的时候,电脑内存爆满,导致电脑卡死,而后重启得以恢复。因此需要想出一个更加高效的方法,另一个思路就是——动态规划算法,我们可以定义dp[i][j],表示长度为i终点为数字j的电话号码个数,由于每一个数字能移动到其他数字的个数是有限的,我们可以将其列举出来:

    终点数字起点数字
    04, 6
    16, 8
    27, 9
    34, 8
    43, 9, 0
    5
    61, 7, 0
    72, 6
    81, 3
    94, 2

    可以发现只有数字5移动到其他数字上,并且其他数字也不能移动到数字5上,因此我们可以设置一个vector数组,将其都存储进去,然后在循环的时候进行遍历这个数组,表示每个值能从那个位置移动而来,如果每一个数字能移动其他数字上,那么反过来也是成立的

    const vector>fromNum = {{4, 6}, {6, 8}, {7, 9}, {4, 8}, {3, 9, 0}, {}, {1, 7, 0}, {2, 6}, {1, 3}, {4, 2}};

    初始化数组,如果电话号码的长度为1,那么每一个数字都只不能移动,所以长度为1的每一个数字的值都为1。即:for(int i = 0; i < 10; i++) dp[1][i] = 1;

    进行动态规划结束后,我们得到的是以每一个数字结尾的长度为n的电话号码个数,只需要将长度为N的每一个数字结尾的电话号码个数进行累加即可得到最终的答案(注意:还要进行取余)。

    代码:

    暴力求解:

    该代码不能使用,会栈溢出,如果使用的话n的值不要超过17,如果超过17,就是不会使内存爆满,也会有漫长的等待时间。

    # include
    # include
    # include
    using namespace std;
    # define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    # define ll long long
    const ll mod = 10e9 + 7;
    
    // 定义工具 
    int x[8] = {2, 2, -2, -2, 1, -1, 1, -1};  // 左右 
    int y[8] = {-1, 1, -1, 1, -2, -2, 2, 2};  // 上下 
    int direction_number = sizeof(x) / sizeof(x[0]);
    // 边界
    int min_x = 0, min_y = 0;
    int max_x = 2, max_y = 3; 
    
    int a[][3] = {
    	{1, 2, 3},
    	{4, 5, 6},
    	{7, 8, 9},
    	{-1, 0, -1}
    };
    set<string> answer;
    void search(int n, string temp_string, int temp_number, int pos_x, int pos_y){
    //	cout<
    	// 如果达到了边界,就进行存入数据,并且返回。 
    	if(temp_number == n){
    		answer.insert(temp_string);
    //		cout<
    		return ;
    	}
    	// 如果没有达到边界,就继续进行递归累加
    	// 一共有八种方向,每一个方向都进行尝试
    	for(int dire = 0;dire < direction_number; dire ++){
    		int temp_x = pos_x + x[dire];
    		int temp_y = pos_y + y[dire];
    		if(temp_x >= min_x && temp_x <= max_x && temp_y >= min_y && temp_y <= max_y){  // 判断是否越界 
    			if(a[temp_x][temp_y] == -1) continue;  		// 只能遍历到数字
    			string num_to_string = to_string(a[temp_x][temp_y]);
    			search(n, temp_string + num_to_string, temp_number + 1, temp_x, temp_y);
    		}
    	}
    	
    	return ;
    }
    
    void solve(){
    //	int n; cin>>n;
    	int n = 14;
    	int rows = sizeof(a) / sizeof(a[0]);
    	int colums = sizeof(a[0]) / sizeof(a[0][0]);
    	// 初始位置可以是任意一个数字的位置
    	for(int row = 0;row < rows; row++){
    		for (int colum = 0; colum < colums; colum ++){
    			if(row == 3 && colum == 0 || row == 3 && colum == 2) continue;
    			string num_to_string = to_string(a[row][colum]);
    			search(n, num_to_string, 1, row, colum);
    		}
    	}
    	
    	cout<<answer.size() % mod<<endl;
    	
    
    //	for(int row = 0;row < rows; row++){
    //		for (int colum = 0; colum < colums; colum ++){
    //			printf("%d ", a[row][colum]);
    //		}
    //
    //	}
    	return ;
    }
    
    signed main(){
    	IOS int t = 1;
    	while(t--){
    		solve();
    	}
    	return 0;
    } 
    

    动态规划:

    调试代码:
    #include
    #include
    using namespace std;
    # define IOS ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    # define ll long long
    
    const ll mod = 1e9 + 7;
    const vector<vector<int>>fromNum = {{4, 6}, {6, 8}, {7, 9}, {4, 8}, {3, 9, 0}, {}, {1, 7, 0}, {2, 6}, {1, 3}, {4, 2}};
    void solve(){
    //	int n;cin>>n;
    	int N = 3131;
    	int ans = 0;
    	vector<vector<ll>>dp(N + 1, vector<ll>(10, 0));
    	// 初始化边界, 一次的电话号码个数 
    	for(int i = 0; i < 10;i ++)dp[1][i] = 1;
    	// 进行动态规划, 从两次开始 
    	for(int n = 2; n<= N;n ++){
    		for(int j = 0;j < 10; j++){
    			for(int from : fromNum[j]){
    				dp[n][j] = (dp[n][j] + dp[n - 1][from]) % mod;
    			}
    		} 
    	}
    //	for(int i = 0;i <= N; i++){
    //		for(int j = 0;j < 10;j ++){
    //			cout<
    //		}
    //		cout<
    //	}
    	for(int i = 0;i < 10;i ++) ans = (ans + dp.back()[i]) % mod;
    	cout<<ans<<endl;
    	
    	return ;
    }
    
    
    int main(){
    	IOS int t = 1;
    	while(t--){
    		solve();
    	}
    	return 0;
    }
    
    提交代码:
    class Solution {
    public:
        const long long mod = 1e9 + 7;
        const vector<vector<int>>fromNum = {{4, 6}, {6, 8}, {7, 9}, {4, 8}, {3, 9, 0}, {}, {1, 7, 0}, {2, 6}, {1, 3}, {4, 2}};
        int knightDialer(int N) {
            // int N = 3131;
            int ans = 0;
            vector<vector<long long>>dp(N + 1, vector<long long>(10, 0));
            // 初始化边界, 一次的电话号码个数 
            for(int i = 0; i < 10;i ++)dp[1][i] = 1;
            // 进行动态规划, 从两次开始 
            for(int n = 2; n<= N;n ++){
                for(int j = 0;j < 10; j++){
                    for(int from : fromNum[j]){
                        dp[n][j] = (dp[n][j] + dp[n - 1][from]) % mod;
                    }
                } 
            }
            for(int i = 0;i < 10;i ++) ans = (ans + dp.back()[i]) % mod;
            
            return ans;
        }
    };
    
    
    
  • 相关阅读:
    代码随想录算法训练营第五十九天|647. 回文子串、 516.最长回文子序列
    Centos7安装MongoDB7.xxNoSQL数据库(骨灰级+保姆级)
    nvme-cli
    Vue 安装 vue的基本使用 vue的初步使用步骤
    开源OA开发平台最新教程:新版消息配置
    MATLAB人工大猩猩部队GTO优化CNN-LSTM用于多变量负荷预测
    21天学习挑战:经典算法---索引查找
    char(10)中的10代表的是字符还是字节
    【iOS】viewController的生命周期
    Android Kotlin 打开相册选择图片(多选)
  • 原文地址:https://blog.csdn.net/qq_73910510/article/details/139997625