1. 程式人生 > >#10006. 「一本通 1.1 練習 2」數列分段

#10006. 「一本通 1.1 練習 2」數列分段

【題目描述】

對於給定的一個長度為 N 的正整數數列 Ai,現要將其分成連續的若干段,並且每段和不超過 M(可以等於 M),問最少能將其分成多少段使得滿足要求。

【輸入格式】

第一行包含兩個正整數 N,M,表示了數列 Ai​ 的長度與每段和的最大值;

第二行包含 NNN 個空格隔開的非負整數 Ai。

【輸出格式】

輸出檔案僅包含一個正整數,輸出最少劃分的段數。

【樣例輸入】

5 6
4 2 4 5 1

【樣例輸出】

3

【資料範圍與提示】

對於 20%的資料,有N≤10;

對於 40% 的資料,有N≤1000;

對於 100% 的資料,有 N≤10^5, M≤10^9,MMM 大於所有數的最大值,Ai之和不超過10^9。

思路:啊呀,這絕對是我做loj的貪心以來最簡單的一道題目了,話不多說。說實在的這道題我一看到就想到了dp,(無奈撓額頭的表情),後面想了一下,其實用貪心更簡單,直接模擬判斷就好了。

判斷有兩個:

1.判斷a[i]的值加起來是否比m大

2.判斷如果加起來比m大,ans++

3.判斷最後的單獨一組

是不是簡單的一批,真的很簡單的啦,其實我覺得就是區域性最優解,就是我們要儘可能的使a[i]+起來的值與m最接近,
解釋完畢,就這麼簡單。

【程式碼實現一:詳解版】

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read()//日常快讀 
{
	char c=getchar();
	int x=0,f=1;
	while(c<48 || c>57)
	{
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>=48 && c<=57)
	{
		x=x*10+c-48;
		c=getchar();
	}
	return x*f;
}
int a[110000];
int main()
{
	int n,m; n=read(); m=read();
	for(int i=1;i<=n;i++) a[i]=read();
	int sum=0;//sum是用來記錄當前累積的數的總和 
	int ans=0;//ans是用來記錄可以成立的組合 
	for(int i=1;i<=n;i++)
	{
		if(sum+a[i]<=m) sum+=a[i];
		/*
			如果當前sum+a[i]的值小於m,就是他的組合和的限值的話
			就把sum更新為當前的a[i]
			為什麼要進行這一步呢,因為題目要求的是最少的組合
			所以我們要儘量是組合和儘可能大 
			所以這裡不能直接加,而是要累加,再判斷 
		*/ 
		else//如果sum+a[i]>m的話,說明我們可以把前面的消除,答案+1 
		{
			ans++;//答案+1 
			sum=a[i];//把前面的清零,從當前的這個a[i]繼續往下算
			/*
				注意很多人容易忘掉這一步,這一步不可以少
				因為這一步代表的就是說我們把前面的清掉
				是代表前面的成為一組,跟當前我們迴圈到的這個a[i]
				沒有半點關係
				所以sum一定一定要更新為a[i]
				再用當前的a[i]去組合
				比如說
				4 2 4 m的值為6
				那麼我們4+2+4跳到了else這一步
				所以我們知道他們的組合不是 4 2 4
				而是4 2,所以就要把最後的4留下,其他的換為ans++ 
			*/ 
		}
	}
	if(sum!=0) ans++;
	/*
		這一步是我重點講解的,前面的都很好理解
		這一步是什麼意思呢?
		就是說我們一直組合組合,發現組合到最後的那一組的時候
		就是剩下的時候,電腦不會給我們自動ans++
		按常理最後剩下沒組合的要自己一個一組
		所以這裡我們就要判斷
		如果按照了前面的之後,i已經沒有的時候
		但是還有數而且比m小的時候
		我們就要單獨判斷sum剩下的值,不是0的話說明還要自成一組,ans++
		
		就拿樣例來說吧
		5 6
		4 2 4 5 1
		前面的4 2先判斷成了一組
		然後4 5的時候成了一組
		這個時候sum的值為5,到i=5,a[i]=1
		但是5+1=6,沒有大於m,所以sum這時變成了6
		但實際上這是要成為一組的
		所以我們就要在這裡判斷剩下的sum值
		剩下了,就ans++,答案加一,組合加一 
	*/
	printf("%d\n",ans);//輸出答案 
	return 0;
}

【程式碼實現二:小小的變化】

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
inline int read()
{
	char c=getchar();
	int x=0,f=1;
	while(c<48 || c>57)
	{
		if(c=='-') f=-1;
		c=getchar();
	}
	while(c>=48 && c<=57)
	{
		x=x*10+c-48;
		c=getchar();
	}
	return x*f;
}
int a[110000];
int main()
{
	int n,m; n=read(); m=read();
	for(int i=1;i<=n;i++) a[i]=read();
	int sum=0;
	int ans=0;
	for(int i=1;i<=n;i++)
	{
		if(sum+a[i]<=m) sum+=a[i];
		else
		{
			ans++;
			sum=a[i];
		}
	}
	printf("%d\n",ans+1);//代表最後的組合 
	return 0;
}

表示蒟蒻並不知道為什麼第二個程式碼會比第一個程式碼多了2ms秒