藍橋杯-合併數列

小程xy發表於2024-05-23

小明發現有很多方案可以把一個很大的正整數拆成若干正整數的和。他採取了其中兩種方案,分別將它們列為兩個陣列 {a1, a2, ..., an} 和 {b1, b2, ..., bm}。兩個陣列的和相同。

定義一次合併操作可以將某陣列內相鄰的兩個數合併為一個新數,新數的值是原來兩個數的和。小明想透過若干次合併操作將兩個陣列變成一模一樣,即 n=m 且對於任意下標 i 滿足 ai=bi。請計算至少需要多少次合併操作可以完成小明的目標。

輸入格式

輸入共 3 行。

第一行為兩個正整數 n, m。

第二行為 n 個由空格隔開的整數 a1, a2, ..., an。

第三行為 m 個由空格隔開的整數 b1, b2, ..., bm。

輸出格式

輸出共 1 行,一個整數。

樣例輸入

4 3
1 2 3 4
1 5 4

樣例輸出

1

樣例說明

只需要將 a2 和 a3 合併,陣列 a 變為 {1, 5, 4},即和 b 相同。

評測用例規模與約定

對於 20% 的資料,保證 n, m ≤ 10^3。

對於 100% 的資料,保證 n, m ≤ 10^5,0 < ai, bi ≤ 10^5。

題解:

這題有兩種寫法, 第一種:模擬佇列, 第二種:字首和+二分

題解一:

模擬佇列法

對於兩個序列 a 和 b 的開頭包含三種情況:

  1. a[0]等於b[0], 此時把兩個開頭都刪除掉
  2. b[0] < a[0], 此時把b[0]和b[1]相加, 然後刪除b[0]和b[1], 把b[0]和b[1]相加的結果放到b的開頭, 相當於是合併b的前兩個數, cnt ++ (cnt是總運算元)
  3. a[0] < b[0], 此時把a[0]和a[1]相加, 然後刪除a[0]和a[1], 把a[0]和a[1]相加的結果放到a的開頭, 相當於是合併a的前兩個數, cnt ++

ac程式碼👇

#include <bits/stdc++.h>
using namespace std;
#define int long long    // 序列中的數最大是1e5, 如果兩個都是1e5, 那麼這兩個數相加會爆int
const int N = 1e5 + 10;
int a[N], b[N];

signed main()
{
	int n, m; cin >> n >> m;
	for (int i = 0; i < n; i ++) cin >> a[i];
	for (int j = 0; j < m; j ++) cin >> b[j];
	
	int i = 0, j = 0, cnt = 0;
	while (i < n && j < m)  // 也可以用dequeue, 但執行效率會低一些
	{
		if (a[i] == b[j]) i ++, j ++;    
		else if (a[i] < b[j]) a[i + 1] = a[i] + a[i + 1], i ++, cnt ++;
		else if (b[j] < a[i]) b[j + 1] = b[j] + b[j + 1], j ++, cnt ++;
	}
	cout << cnt << endl;
	return 0;
}

題解二:

字首和+二分法

  • 先對兩個序列都求一次字首和
  • 當字首和相同的時候跳過, 不同的時候分為兩種情況:
  • a<b的時候, 用二分查詢一下"第一個等於b的值得下標", 然後加上操作次數; b<a的時候, 用二分查詢一下"第一個等於a的值得下標", 然後加上操作次數
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1e5 + 10;  // 會爆int
int a[N], b[N];

signed main()
{
	int n, m; cin >> n >> m;
	for (int i = 1; i <= n; i ++) 
	{
		cin >> a[i];
		a[i] += a[i - 1];    // 求字首和
	}
	for (int j = 1; j <= m; j ++) 
	{
		cin >> b[j];
		b[j] += b[j - 1];  // 求字首和
	}
	
	int i = 1, j = 1, cnt = 0;
	while (i <= n && j <= m) 
	{
		if (a[i] == b[j]) i ++, j ++;
		
		else if (a[i] < b[j])
		{
			int l = i, r = n;
			while (l < r)
			{
				int mid = l + r >> 1;
				if (a[mid] >= b[j]) r = mid;	// 找到的是第一個滿足 條件的下標 
				else l = mid + 1;
			}
			cnt += l - i;
			i = l;
			
		}
		
		else if (b[j] < a[i])
		{
			int l = j, r = m;
			while (l < r)
			{
				int mid = l + r >> 1;
				if (b[mid] >= a[i]) r = mid;  // 找到的是第一個滿足 條件的下標
				else l = mid + 1;
			}
			cnt += l - j;
			j = l;
			
		}
	}
	cout << cnt << endl;
	return 0;
}

覺得寫的不錯的話, 點個贊吧~