基礎算法題——帶分數(全排列,工具庫)

前言
這道題理解起來不難,但是要找到一個合適的方法對題目進行優化,就會相對麻煩些。
藍橋杯的題,真的到處都是坑的感覺。。。

帶分數題目

資源限制
時間限制:1.0s 內存限制:256.0MB
問題描述
100 可以表示爲帶分數的形式:100 = 3 + 69258 / 714。
還可以表示爲:100 = 82 + 3546 / 197。
注意特徵:帶分數中,數字1~9分別出現且只出現一次(不包含0)。
類似這樣的帶分數,100 有 11 種表示法。
輸入格式
從標準輸入讀入一個正整數N (N<1000*1000)
輸出格式
程序輸出該數字用數碼1~9不重複不遺漏地組成帶分數表示的全部種數。
注意:不要求輸出每個表示,只統計有多少表示法!
樣例輸入1
100
樣例輸出1
11
樣例輸入2
105
樣例輸出2
6


題目分析

①、帶分數中,數字1~9分別出現且只出現一次(不包含0)。
②、100 可以表示爲帶分數的形式:100 = 3 + 69258 / 714。

解題思路

①:如何實現在帶分數中,數字1~9分別出現且只出現一次。我們可以考慮全排列,將每種排列都來一遍(感覺挺麻煩的,如果有更好的方法歡迎留言討論)。
②:在例子中 100 = 3 + 69258 / 714,“3 + 69258 / 714”部分要符合數字1~9分別出現且只出現一次,這時可以用到我們字符串截取。拿上面的例子說,排列爲369258714,我們截取了字符串3、字符串69258、字符串714,然後將三個字符串轉換爲int類型變量,看看是否符合條件(100 = 3 + 69258 / 714)符合則記錄下來。

實用工具功能及使用

實現全排列:next_permutation(s.begin(), s.end());

在C++中,在字符串要有序的前提下,我們能使用現成的函數完成字符串的全排列。例如:string s=“123456789”;,字符串s已經有序。通過 while(next_permutation(s.begin(), s.end())); 不斷將s進行全排序,當s的全排序全部排完後,跳出循環。可能你會覺得這個有什麼用?讓我們帶着這個問題看看代碼設計吧!

string s="123456789";
do
{
	....
}
while(next_permutation(s.begin(), s.end()));

我們這裏用do-while的循環體,保證s每進行一次全排列就會先進行do包含的語句。

實現字符串截取:string a = s.substr(i,len);

在s字符串中,從下標爲i的位置,向右截取len長度的字符串,得到a字符串。

實現字符串轉換爲int類型變量:int a = atoi(s.c_str);

將字符串s轉換爲int類型變量a。


代碼實現

代碼一

通過我們的解題思路和工具結合,我們可以得到以下代碼一。

//全排列 next_permutation(s.begin(),s.end())
#include<bits/stdc++.h>
#include<time.h>
using namespace std;
#define ll long long
ll ans=0;

int main()
{
//	clock_t start, finish;
//	start = clock();
	int n;
	string s="123456789";
//	sort(s,s+9);
	cin>>n;
	
	do
	{
		for(int i=1; i<=7; i++)
		{
			string a=s.substr(0,i);
			int inta=atoi(a.c_str());
			if(inta>=n) break;
			
			for(int j=1; j<=8-i; j++)
			{
				string b=s.substr(i, j);
				int intb=atoi(b.c_str());
				string c=s.substr(j+i);
				int intc=atoi(c.c_str());
				if(intb%intc==0&&inta+intb/intc==n)
				ans++;
			}
		}
	}
	//使用next_permutation前要先排序  
	while(next_permutation(s.begin(), s.end()));
	cout<<ans;
//	finish = clock();
//	double _time = (double)(finish - start) / CLOCKS_PER_SEC;
//	printf("\ntime:%llf", _time);
	return 0;
}

好啦,恭喜你答對了題目一半,爲什麼是一半呢?因爲如果你拿去評測的話, 你會發現:運行超時!
下面是代碼二在N=100條件下的運行時間time (單位秒)
代碼一運行時間
原因:substr函數返回的是值(而不是引用或指針) 在多次調用中傳遞了大量對象(而不是引用或指針) 導致了耗時多
在代碼裏,substr函數被放在內層循環中,被調用的次數很多,這樣的對程序運行總時間的影響就增大了,拖慢了程序運行速度。

代碼二(手寫截取字符串,引用字符串s)
#include<bits/stdc++.h>
#include<time.h>
using namespace std;
#define ll long long
ll ans=0;

int parse(string &s, int a, int b)//字符串轉換爲int類型 
{
	int temp=0;
	for(int i=a; i<b; i++)
	{
		temp+=s[i]-'0';
		if(i<b-1)
			temp*=10;
	}
	return temp;
}

int main()
{
//	clock_t start, finish;
//	start = clock();
	int N;
	string s="123456789";
//	sort(s,s+9);
	cin>>N; 
	
	do
	{//	1234567+8/9 
		for(int i=1; i<=7; i++)			//分割第一個數據長度 
		{
			int inta = parse(s, 0, i);
			if(inta>N) break;
			//1+2345678/9
			//12+345678/9 
			//1+2/3456789
			for(int j=1; j<=8-i; j++)		//分割第二個數據長度
			{
				int intb=parse(s, i, j+i);
				int intc=parse(s, j+i, s.length());//分割第三個數據
				if(intb%intc==0&&inta+intb/intc==N)
				{
//					cout<<inta<<"+"<<intb<<"/"<<intc<<endl;
					ans++;
				}
			}
		} 
	}
	while(next_permutation(s.begin(), s.end()));
	cout<<ans;
//	finish = clock();
//	double _time = (double)(finish - start) / CLOCKS_PER_SEC;
//	printf("\ntime:%llf", _time);
	return 0;
}

優化:截取字符串函數傳參採用傳指針或引用(即在parse 的string參數右側加一個&)。
下面是代碼二在N=100條件下的運行時間time (單位秒)
代碼二運行結果
時間上滿足1s內,可以通過評測。
下面還能夠再通過優化算法的方式,進一步縮短運行時間。

代碼三(算法優化)
#include<bits/stdc++.h>
#include<time.h>
using namespace std;
#define ll long long
ll ans=0;

int parse(string& s, int a, int b)//字符串轉換爲int類型 
{
	int temp=0;
	for(int i=a; i<b; i++)
	{
		temp+=s[i]-'0';
		if(i<b-1)
			temp*=10;
	}
	return temp;
}

int main()
{
//	clock_t start, finish;
//	start = clock();
	int N;
	string s="123456789";
//	sort(s,s+9);
//	N=100;
	cin>>N;
	
	do
	{
		for(int i=1; i<=7; i++)			
		{
			int inta = parse(s, 0, i);
			if(inta>N) break;
			//1+2345678/9
			//12+345678/9 
			//1+2/3456789
			for(int j=1; j<=8-i; j++)	
			{
				if(j<9-i-j) continue;	//排除多餘情況
				int intb=parse(s, i, j+i);
				int intc=parse(s, j+i, s.length());
				if(intb%intc==0&&inta+intb/intc==N)
				{
//					cout<<inta<<"+"<<intb<<"/"<<intc<<endl;
					ans++;
				}
			}
		} 
	}
	while(next_permutation(s.begin(), s.end()));
	cout<<ans;
	
//	finish = clock();
//	double _time = (double)(finish - start) / CLOCKS_PER_SEC;
//	printf("\ntime:%llf", _time);
	return 0;
}

優化:排除多餘判斷情況。
下面是代碼三在N=100條件下的運行時間time(單位秒)
運行時間

總結

這道題思路固然重要,但是優化也很重要。
在藍橋杯的比賽中,都是提交代碼,我們不能都提前判斷我們的代碼是否能夠通過所有評測數據,所以要儘量將時間複雜度降到最小。

感謝評論區的朋友,對我的文章提出指正√
希望能夠將自己的一些學習經驗分享給有需要的人。
我是小鄭,一個堅持不懈的小白。

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