问题描述
每次打靶会有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
更优化一点
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 |
由上有,找规律 |