NOIp2001 數的計算

【遞推||回溯】NOIp2001普及組 數的計算

題目鏈接

第一次看竟然沒有思路,看來還是我太菜了
  這題可以用遞歸(回溯)做,也可以用遞推做。但是思路都是一樣的。這裏的重點是當你在左邊填了一個數後,下一次填數是以你填的這個數爲基礎的,而不是之前的原數。
  現在,對於一個數n,我在他的左邊可以填1…n/2(注意這裏一半也能填),那麼填完後就變成了求我填的這個數有幾種拓展的方法數,因此就分解成了規模更小的幾個子問題
  光說可能不一定能理解,蒟蒻就拿給的樣例來解釋一下思路
  輸入n = 6,那麼6左邊填完後可能是16,26,36吧,1,2,3就對應了1…n/2吧。所以n = 6就被轉換成了n = 1,n = 2,n = 3這三種方法的情況之和。對於n = 1、2、3的情況,我們還以相同的方式去求,最後就能求出答案。
  說白了並不難(畢竟只是道橙題),下面給出DFS(回溯)的代碼:

//DFS版:注意:會TLE
#include<iostream>
#include<cstdio>
int f(int); 
using namespace std;
int n;
int main(){
	ios::sync_with_stdio(false);
	cin >> n;
	cout << f(n) << endl;
	return 0;
}
int f(int n){
	//n的左邊可以填1 ~ n/2
	int cnt = 1; //注意,n本身也算一種,所以初始爲1 
	for(int i = 1;i <= n/2;i++){
		cnt += f(i);
	} 
	return cnt;
}

  雖然n最大是1000,看似不大,但是dfs會超時。因爲我們算了很多次參數重複的情況。當子問題重複時,我們可以用記憶化搜索的方式去改進,但是這題記憶化和遞推區別不大,所以我們再用遞推的方式去改寫代碼。這題改成遞推就是算出一個就保存,這樣以後用到的時候可以直接用,而不是像dfs再算一遍(好像這個各位大佬早就明白了?/捂臉)
  遞推版本的AC代碼如下:

#include<iostream>
#include<cstdio>
using namespace std;
int n,ans[1010]; //ans[i]是i所能拓展出的個數,那麼ans[n]就是答案 
int main(){
	ios::sync_with_stdio(false);  
	cin >> n;
	ans[1] = 1;
	for(int i = 2;i <= n;i++){
		ans[i] = 1; //自己算一種 
		for(int j = 1;j*2 <= i;j++){
			ans[i] += ans[j];
		}
	}
	cout << ans[n] << endl; 
	
	return 0;
} 

又AC了題單的一道題呢

蒟蒻發文,歡迎各位大佬多多提建議~

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