宇宙条面试题-枪手打靶问题

问题描述

每次打靶会有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

由上有,找规律

原文地址:https://www.cnblogs.com/shuiyonglewodezzzzz/p/13252619.html