1. 程式人生 > >模擬題——數17 的簡單解法

模擬題——數17 的簡單解法

數17 Problem Description 小cos是寒樹中學一名機敏睿智的男生。最近他偶然地發現同班的小cot——極為神祕的女生——正想方設法地收集他的資訊。小cos非常不解。這天他收到了來自小cot的一封郵件,要他數出從a到b(包括a和b)的正整數中共有多少個“17”。(如817517有兩個“17”) 小cot打著什麼算盤?“17”蘊含著什麼不為人知的祕密?……不,你不需要管這些……你的任務是回答小cot的問題。 小cos是寒樹中學一名機敏睿智的男生。最近他偶然地發現同班的小cot——極為神祕的女生——正想方設法地收集他的資訊。小cos非常不解。這天他收到了來自小cot的一封郵件,要他數出從a到b(包括a和b)的正整數中共有多少個"17"。(如817517有兩個"17") 小cot打著什麼算盤?"17"蘊含著什麼不為人知的祕密?……不,你不需要管這些……你的任務是回答小cot的問題。 Input 第1行兩個正整數a和b,意義見問題描述。 Output 輸出一個整數,表示a到b的正整數中"17"的數量。 Sample Input 1 200 Sample Output 12 Data Constraint 對於30%的資料,有a≤b≤1,000 對於50%的資料,有a≤b≤100,000 對於100%的資料,有a≤b,a,b均在 int 範圍內。

首先,看到題目,我們很容易知道,它詢問的是,對於一個區間 [a,b],裡面所有的數中,有多少個"17",例如 117 有1個"17", 1717171 有6個"17"。 那麼,我們很容易做出一個簡單的暴力程式,計算出一個數有多少個"17"。 程式碼如下。

#include <cstdio>

#define LL long long 

using namespace std;

LL a,b,ans;

LL find(LL a)
{
	LL p=0;
	while (a)
	{
		if (a%100==17)
			p++,a/=100;
		else
			a/=10;	
	}
	return p;
}

int main()
{
	scanf("%lld%lld",&a,&b);
	for (LL k=a;k<=b;k++)
		ans+=find(k);
	printf("%lld",ans);
	return 0;
}

這個程式可以幫助我們做幾件事。

  • 直接作為程式碼提交可以拿到部分分。
  • 可以用計算機暴力經過一段時間,以此求出所有情況,然後用分塊的方法暴力來大大降低複雜度。例如,如果我們將17的數量以105分塊,那麼,我們的複雜度最大就是 2*105+104,也就是所有分塊的求和,以及左右兩端這兩塊的單獨求和。那麼,這個複雜度就相當的低了。因為int最大值為2147483647(231-1)
  • 其次,這可以作為我們正確演算法的一個驗算的根據。在我們演算法的正確性不能得到保障的情況下,經過多次大資料的驗算是一個非常有效的驗證演算法正確性的方法。

所以,我們就開始思考正確方法。 我們知道,如果我們要判斷一個數x有沒有17,那麼判斷的次數應該是β(β=x的位數)。而對於a~b的數都要進行判斷,很顯然,這種方法是極其不優的。 那麼,我們可以逆向思維。我們不選擇計算一個一個數含有"17",而是反過來,我們列舉每兩個相鄰的位上的"17",並計算出在這兩個位是"17"的情況下,有多少個數滿足條件。而這樣,我們就可以運用組合數學的方法進行進一步處理。 當然,補充一下,我們確定兩位是"17"後,計算出[a,b]總共有多少個數滿足這兩位是"17",也可以擴充套件一下,就是[1,b]有多少個數滿足這兩位是"17",並用它減去[1,a-1]有多少個數滿足這兩位是"17"。 因此,我們不斷列舉兩個位是"17",並且計算出此時滿足在[1,b]、[1,a-1]上的數的值並且不斷做差。 那麼,我們就可以通過這樣的值進行求和。 不過,我們需要明確的是,對於一個數1717在[a,b]範圍內,我們列舉百位、前位是"17"時,就會考慮到這個數1717,而在我們列舉個位、十位是"17"時,也會考慮到這個數1717,那麼,它就被考慮了兩次,正好與其1717有兩個"17"相應,所以,我們可以很確定的知道,我們只需要列舉"17"的位置,而無需在意考慮的數是否還有別的"17",因為其如果還有別的"17",必然會在另外的列舉中被考慮到。 所以,我們可以寫出如下程式碼。

#include<cstdio>

#define LL long long 

using namespace std;


LL c[14];
LL ans,a;

LL find(LL );

int main()
{
	c[0]=c[1]=1;
	for (LL k=2;k<=13;k++)
		c[k]=c[k-1]*10;
	scanf("%lld",&a);
	a--;
	ans=find(a);
	scanf("%lld",&a);
	printf("%lld",find(a)-ans);
	return 0;
}

LL find(LL x)
{
	if (x<17)
		return 0;
	LL m=13,out=0;
	while (c[m]>x)
		m--;
	m++;
	LL y,now;
	for (LL k=m;k>=3;k--)
	{
		y=x%c[k];
		now=y/c[k-2];
		out+=(x/c[k])*c[k-2];
		if (now>17)
			out+=c[k-2];
		else
			if (now==17)
				out+=(k==3?1:((y%c[k-2])+1));
	}
	return out;
}