uva 10328 - Coin Toss(计数问题)

题目链接: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;
}


你可能感兴趣的:(uva 10328 - Coin Toss(计数问题))