zoukankan      html  css  js  c++  java
  • 宇宙条面试题-枪手打靶问题

    问题描述

    每次打靶会有0到10的得分,问打10次恰好得90分有多少种打法

    问题简化

    每次打靶会有0到2分的得分,问打3次恰好得4分有多少种打法

    分析

    打一次

    得分情况组合方式
    0分(0)

    1分(1)

    2分(2)

    打俩次

    得分情况组合方式

    0分 (0,0)

    1分 (1,0),(0,1)

    2分 (2,0),(1,1),(0,2)

    3分 (2,1),(1,2)

    4分(2,2)

    由上可以看出,计算俩枪打完后得分组合数时,必须先把第一枪的得分组合数计算出来。

    第二枪开完后得2分的组合数

    第一枪得0分组合数 + 第一枪得1分得组合数 + 第二枪得2分得组合数

    第二枪开完后得3分的组合数

    第一枪 得2分组合数 + 第一枪得1分得组合数

    由此有下面表格

    1、行表示开枪的次数

    2、列表示组合数

    3、列下表表示得分

    0 1 2 3 4 5 6
    0 枪 0 0 0 0 0 0 0
    1枪 1 1 0 0 0 0 0
    2枪 1 2 3 2 1 0 0
    3枪 1 3 6 7 6 3 1

    泛化

    每次打靶会有0到k分的得分,问打i次恰好得j分有多少种打法

    条件约束 0<=j<=k*i

    1、每一枪得分范围是0-k

    2、i枪得分范围是i*k

    3、( i - 1 )枪得分范围是 k *( i - 1)

    设 C[i][j] 表示 打完i枪后,得分恰好是j分的打法

    C [ i ][ j ] = C[ i - 1 ][ j - 0 ] + C[i-1][j-1]+.......+C[i-1][ j - k ]

    公式含义

    C[ i - 1 ][ j ] 是i-1枪总得j分,第i枪得0分

    C[i-1][j-1] 是i-1枪总的j-1分,第i枪得1分

    C[i-1][ j - k] 是i-1枪得j-k分,第i枪得k分

    由此有代码如下

    #include <string>
    #include<iostream>
    #include <sstream>
    #include<map>
    #include<set>
    #include<memory.h>
    #include<vector>
    #include<algorithm>
    #include<queue>
    #include<vector>
    #include<stack>
    #include<math.h>
    #include<iomanip>
    #include<bitset>
    #include"math.h"
    namespace cc
    {
    	using std::cout;
    	using std::endl;
    	using std::cin;
    	using std::map;
    	using std::set;
    	using std::vector;
    	using std::string;
    	using std::sort;
    	using std::priority_queue;
    	using std::greater;
    	using std::vector;
    	using std::swap;
    	using std::stack;
    	using std::bitset;
    	using std::stringstream;
    	using std::abs;
    
    	int solve(int num, int score) {
    		const int MAX_SCORE = 100;
    		const int MAX_NUM = 10;
    		int c[MAX_NUM][MAX_SCORE];
    		//一枪可以得分0-k
    		//const int k = 9;
    		
    		const int k = 2;
    		if (k * num < score || score < 0 || score >= MAX_SCORE) {
    			cout << "error input" << endl;
    			return -1;
    		}
    		memset(c, 0, sizeof(c));
    		//
    		for (int j = 0; j <= k; j++) {
    			//第一枪
    			c[1][j] = 1;
    		}
    		//得分0
    		for (int j = 1; j <= num; j++) {
    			c[j][0] = 1;
    		}
    
    		//第i枪
    		for (int i = 2; i <= num;i++) {
    			//当前最大得分是i * k 
    			int maxScore = i * k;
    			//j分
    			for (int j = 1; j <= maxScore;j++) {
    				//当前j得分组合数
    				int sum = 0;
    				for (int t = 0; t <= k; t++) {
    					sum = sum + c[i - 1][j - t];
    				}
    				c[i][j] = sum;
    			}
    		}
    		return c[num][score];
    	}
    }
    
    
    int main()
    {
    
    #ifndef ONLINE_JUDGE
    	freopen("d://1.text", "r", stdin);
    	//freopen("d://1.out","w",stdout);
    #endif // !ONLINE_JUDGE
    	int num = 3, score = 5;
    	int ans = cc::solve(num,score);
    	std::cout << ans << std::endl;
    	return 0;
    }
    

    优化

    公式1

    C [ i ][ j ] = C[ i - 1 ][ j ] + C[i-1][j-1]+......+C[i-1][ j - (k-1)]+C[i-1][ j - k]

    忽略下标i,项的排列如下

       	j, j-1,  j-2,  j-3....  j-(k-1),j-k
    

    当j=j+1时

    项的排列如下

    j+1,   (j+1)-1,  (j+1)-2,(j+1)-3,(j+1)-4..... (j +1) - (k-1),(j+1) - k
    

    化简有

    j+1,   j,  j-1,j-1,..... j-(k-2),j - (k-1)
    

    所以有

    C [i][j+1] = C[i-1][j+1] + C[i][j] - C[i-1][j+1-(k+1)]

    C [i][j] = C[i-1][j] + C[i][j-1] - C[i-1][j-(k+1)]

    代码如下:

    #include "pch.h"
    #include 
    #include
    #include 
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include
    #include"math.h"
    namespace cc
    {
    	using std::cout;
    	using std::endl;
    	using std::cin;
    	using std::map;
    	using std::set;
    	using std::vector;
    	using std::string;
    	using std::sort;
    	using std::priority_queue;
    	using std::greater;
    	using std::vector;
    	using std::swap;
    	using std::stack;
    	using std::bitset;
    	using std::stringstream;
    	using std::abs;
    
    	int solve(int num, int score,int k) {
    		const int MAX_SCORE = 1000;
    		const int MAX_NUM = 20;
    		int c[MAX_NUM][MAX_SCORE];
    		//一枪可以得分0-k
    		if (k * num < score || score < 0 ) {
    			cout << "error input" << endl;
    			return -1;
    		}
    		memset(c, 0, sizeof(c));
    		//
    		for (int j = 0; j <= k; j++) {
    			//第一枪
    			c[1][j] = 1;
    		}
    		//得分0
    		for (int j = 1; j <= num; j++) {
    			c[j][0] = 1;
    		}
    
    		//第i枪
    		for (int i = 2; i <= num; i++) {
    			//当前最大得分是i * k 
    			int maxScore = i * k;
    			//j分
    			for (int j = 1; j <= maxScore; j++) {
    				//当前j得分组合数
    				int sum = 0;
    				//枚举第i次得分
    				for (int t = 0; t <= k; t++) {
    					if (j-t>=0) {
    						sum = sum + c[i - 1][j - t];
    					}
    				}
    				c[i][j] = sum;
    			}
    		}
    		return c[num][score];
    	}
    
    
    	int solve2(int num, int score,int k) {
    		const int MAX_SCORE = 1000;
    		const int MAX_NUM = 20;
    		int c[MAX_NUM][MAX_SCORE];
    		//一枪可以得分0-k
    		if (k * num < score || score < 0) {
    			cout << "error input" << endl;
    			return -1;
    		}
    		memset(c, 0, sizeof(c));
    		//
    		for (int j = 0; j <= k; j++) {
    			//第一枪
    			c[1][j] = 1;
    		}
    		//得分0
    		for (int j = 1; j <= num; j++) {
    			c[j][0] = 1;
    		}
    
    		//第i枪
    		for (int i = 2; i <= num; i++) {
    			//当前最大得分是i * k 
    			int maxScore = i * k;
    			//j分
    			for (int j = 1; j <= maxScore; j++) {
    				//当前j得分组合数
    				int d = 0;
    				if (j - k -1 >= 0) {
    					d = c[i - 1][j - k-1];
    				}
    				c[i][j] = c[i - 1][j] + c[i][j - 1] - d;
    
    			}
    		}
    		return c[num][score];
    	}
    }
    
    
    int main()
    {
    
    #ifndef ONLINE_JUDGE
    	freopen("d://1.text", "r", stdin);
    	//freopen("d://1.out","w",stdout);
    #endif // !ONLINE_JUDGE
    	int num = 10, score = 90;
    	int k = 10;
    	int ans=0;
    	ans = cc::solve(num, score,k);
    	std::cout << ans << std::endl;
    	ans = cc::solve2(num, score,k);
    	std::cout << ans << std::endl;
    	return 0;
    }
    

    更优化一点

    0 1 2 3 4 5 6
    0 枪 0 0 0 0 0 0 0
    1枪 1 1 0 0 0 0 0
    2枪 1 2 3 2 1 0 0
    3枪 1 3 6 7 6 3 1

    由上有,找规律

  • 相关阅读:
    chmod命令
    ls命令
    数组值去重-小技巧
    使用redis接管session
    使用redis接管cookie
    redis操作封装类
    HTTP协议详解
    Linux网络编程--多播
    Live555 分析(三):客服端
    Live555 分析(二):服务端
  • 原文地址:https://www.cnblogs.com/shuiyonglewodezzzzz/p/13252619.html
Copyright © 2011-2022 走看看