CCF201312-4 有趣的數 不會DP只會枚舉

========================================= 原題 ============================================

問題描述
  我們把一個數稱爲有趣的,當且僅當:
  1. 它的數字只包含0, 1, 2, 3,且這四個數字都出現過至少一次。
  2. 所有的0都出現在所有的1之前,而所有的2都出現在所有的3之前。
  3. 最高位數字不爲0。
  因此,符合我們定義的最小的有趣的數是2013。除此以外,4位的有趣的數還有兩個:2031和2301。
  請計算恰好有n位的有趣的數的個數。由於答案可能非常大,只需要輸出答案除以1000000007的餘數。
輸入格式
  輸入只有一行,包括恰好一個正整數n (4 ≤ n ≤ 1000)。
輸出格式
  輸出只有一行,包括恰好n 位的整數中有趣的數的個數除以1000000007的餘數。
樣例輸入
4
樣例輸出
3

========================================= 思路 ============================================

聽聞網上大牛動不動就數位DP...寶寶做不到啊 =、=

看到這個題想了半天遞推公式,感覺不是很麻煩但就是不想推...然後就有了下面這種亂七八糟的解法:

首先看到題發現了兩個問題:

1、0/1和2/3之間沒有特殊聯繫

2、數字首位必須是2

先解釋下首位2的問題:如題0/2必須在1/3前面,0/1/2/3必須出現至少一次,所以首位只能是0/2(否則總有一個無法出現),但0不能做首位,所以首位必定是2。

然後就是最關鍵的一點——首個1首個3的位置問題。

關於一個n位有趣數的可能情況如果直接寫可能非常複雜,即便按照遞推式也很麻煩,但只關注首個1/3就簡單多了:1出現的比3早、3出現的比1早。

假設首個1出現在第 i 位,首個3出現在第 j 位。

① 1出現的比3早(i < j)


由假設可知,在0~i 位之間沒有1/3只有0/2,在 i ~ j 之間只有1/2,在 j ~ n 之間只有1/3,出去固定位置(0、i、j)外每個位置都有兩種可能,共2^(n-3)種可能。其中存在不符合要求的可能性——全是2沒有0——i 之前沒有0,i之後不影響。所以減去錯誤可能2^(n-i-1),所得結果即爲當前i、j情況下結果總數。

② 3出現的比1早(i > j)

情況其實和前一種類似,只是考慮錯誤情況時候的計算變成了2^(n-i)種可能了(3在前面)。

知道了每種情況下的錯誤計算,還需要注意一下總數的問題:這裏總結果數取的是【1/3可能數 × 0/2可能數 - 錯誤數】,其中1/3可能因爲3的位置出現區別(3可以出現在第2位但1不可以,否則0無法出現)。

下面是AC代碼(附帶兩個防溢出的函數):

#define MOD 1000000007

#include<iostream>
using namespace std;

int getnum(int n){
	int k = 1;
	for (int i = 1; i <= n; i++){
		k *= 2;
		k %= MOD;
	}
	return k;
}

int mul(int a, int b){
	int k = 0;
	for (int i = 0; i < b; i++){
		k += a;
		k %= MOD;
	}
	return k;
}

int main(){
	int n;
	cin >> n;
	long long n13 = ((n - 2)*(n - 3) + n - 2) % MOD;
	long long n02 = getnum(n-3);
	long long ans = (n13*n02) % MOD;
	// 這裏直接把3出現的可能乘進去了
	// 因爲2必定出現所以不用單獨考慮3的錯誤
	// 首個1出現在首個3之前
	for (int i = 3; i < n; i++){
		if (n - i - 1 >= 0){
			int wr = mul(getnum(n - i - 1), n - i);
			ans -= wr;
			// 分步取模可能出現計算後負數
			while (ans <= 0){
				ans += MOD;
			}
		}
	}
	// 首個3出現在首個1之前
	for (int i = 3; i <= n; i++){
		if (n - i >= 0){
			int wr = mul(getnum(n - i), i - 2);
			ans -= wr;
			while (ans <= 0){
				ans += MOD;
			}
		}
	}
	cout << ans%MOD << endl;
	return 0;
}



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章