题目链接:uva 10328 - Coin Toss
题目大意:给出n,表示有投掷n次硬币,硬币有分正反面,玩游戏的人比较迷信,如果他选择正面获胜的话,第二次他还是会选正面(不知道这句话要说什么,应该是背景),然后给出k,问说出现连续正面的此处大于 等于k的情况有多少种,比如说:3 2,投掷3次或有8中情况,满足说有两个以上连续的'H'(正面)的情况只有“HHH”, “HHT”, “THH”,3种, “HTH”虽然有次正面,但是两次不是连续的。
解题思路:一开始题目理解错了,以为是问说投掷n次至少有k次为正面的情况,那不就是简单的随机数吗,然后案例跑不过,就在度了一遍题目,发现是要连续的正面,很明显递推,但是算不出递推公式,后来看了下题解。
首先是逆向思维,c[i][j]表示的是投掷i次,连续正面的个数最多不超过j次的情况有多少种,然后c[i][j] = c[i - 1][j] * 2,无非是第i次增加了两种情况,正面和反面,但是这样计算是要有些不符合条件的情况也考虑进去了,即c[i-1][j]会有说后面的j个为正面,那么在增加一个正面的话连续正面的个数就会变成j + 1个,所以要减去这种情况的个数。
投掷i - 1次的话,连续正面的个数不超过j个的话,又要说最后j个都为正面,那么第i - j - 1个就一定是反面(对应这类情况而言,并不是说i - j - 1为反面就一定不符合),那么只要满足前i - j - 2次中连续正面的个数不超过j的话,就是 要减掉的那部分。
搞清楚思路后想用double偷懒的,但是WA了,还是果断大数吧。
#include <stdio.h> #include <string.h> #include <iostream> using namespace std; const int N = 105; struct bign { int len, sex; int s[2000]; bign() { this -> len = 1; this -> sex = 0; memset(s, 0, sizeof(s)); } bign operator = (const char *number) { int begin = 0; len = 0; sex = 1; if (number[begin] == '-') { sex = -1; begin++; } else if (number[begin] == '+') begin++; for (int j = begin; number[j]; j++) s[len++] = number[j] - '0'; } bign operator = (int number) { char string[N]; sprintf(string, "%d", number); *this = string; return *this; } bign (int number) {*this = number;} bign (const char* number) {*this = number;} bign change(bign cur) { bign now; now = cur; for (int i = 0; i < cur.len; i++) now.s[i] = cur.s[cur.len - i - 1]; return now; } void delZore() { // 删除前导0. bign now = change(*this); while (now.s[now.len - 1] == 0 && now.len > 1) { now.len--; } *this = change(now); } void put() { // 输出数值。 delZore(); if (sex < 0 && (len != 1 || s[0] != 0)) cout << "-"; for (int i = 0; i < len; i++) cout << s[i]; } bign operator + (const bign &cur){ bign sum, a, b; sum.len = 0; a = a.change(*this); b = b.change(cur); for (int i = 0, g = 0; g || i < a.len || i < b.len; i++){ int x = g; if (i < a.len) x += a.s[i]; if (i < b.len) x += b.s[i]; sum.s[sum.len++] = x % 10; g = x / 10; } return sum.change(sum); } bign operator - (const bign &cur) { bign sum, a, b; sum.len = len; a = a.change(*this); b = b.change(cur); for (int i = 0; i < b.len; i++) { sum.s[i] = a.s[i] - b.s[i] + sum.s[i]; if (sum.s[i] < 0) { sum.s[i] += 10; sum.s[i + 1]--; } } for (int i = b.len; i < a.len; i++) { sum.s[i] += a.s[i]; if (sum.s[i] < 0) { sum.s[i] += 10; sum.s[i + 1]--; } } return sum.change(sum); } }; bign c[N][N], sum[N], tmp = 1; void init() { sum[0] = 1; for (int i = 1; i <= 100; i++) sum[i] = sum[i - 1] + sum[i - 1]; for (int i = 0; i <= 100; i++) c[i][0] = c[0][i] = 1; for (int i = 1; i < N; i++) { for (int j = 1; j < N; j++) { c[i][j] = c[i - 1][j] + c[i - 1][j]; if (i == j + 1) c[i][j] = c[i][j] - tmp; else if (i > j + 1) c[i][j] = c[i][j] - c[i - j - 2][j]; } } } int main () { init(); int n, k; while (scanf("%d%d", &n, &k) == 2) { bign ans = sum[n] - c[n][k - 1]; ans.put(); printf("\n"); } return 0; }