洛谷P1209修理牛棚 Barn Repair

Tomorrowland_D發表於2024-08-06

[USACO1.3] 修理牛棚 Barn Repair

題目描述

在一個月黑風高的暴風雨夜,Farmer John 的牛棚的屋頂、門被吹飛了 好在許多牛正在度假,所以牛棚沒有住滿。

牛棚一個緊挨著另一個被排成一行,牛就住在裡面過夜。有些牛棚裡有牛,有些沒有。 所有的牛棚有相同的寬度。 寬度為1

自門遺失以後,Farmer John 必須儘快在牛棚之前豎立起新的木板。他的新木材供應商將會供應他任何他想要的長度,但是吝嗇的供應商只能提供有限數目的木板。 Farmer John 想將他購買的木板總長度減到最少。

給出 \(m,s,c\),表示木板最大的數目、牛棚的總數、牛的總數;以及每頭牛所在牛棚的編號,請算出攔住所有有牛的牛棚所需木板的最小總長度。

輸入格式

一行三個整數 \(m,s,c\),意義如題目描述。
接下來 \(c\) 行,每行包含一個整數,表示牛所佔的牛棚的編號。

輸出格式

輸出一行一個整數,表示所需木板的最小總長度。

樣例 #1

樣例輸入 #1

4 50 18
3 
4 
6 
8 
14
15 
16 
17 
21
25 
26 
27 
30 
31 
40 
41 
42 
43

樣例輸出 #1

25

提示

【資料範圍】
對於 \(100\%\) 的資料,\(1\le m \le 50\)\(1\le c \le s \le 200\)

USACO Training Section 1.3

思路:

  • 題目很多資訊,我們首先捋清楚題目的意思。s與解題無關,我們應該剔除沒用的資訊,供應商最多可以提供m塊板子,有c個牛棚中住了牛,我們需要對這c個牛棚進行修補,每個牛棚需要長度為1的板子,但是我們的總板子數目不超過m個,因此,當m<c時,我們需要對有些牛棚的板子進行延長,比如我們對編號為2,3的兩個牛棚,我們使用一塊長度為2的木板來修復它,直到我們使用的木板總數等於m為止。
  • 我們可以有一個思路,先對每個有牛的牛棚進行分配一塊長度為1的木板,如果供應的木板總數≥牛的數量,那麼我們的答案就是c,每個牛棚分配一塊長度為1的木板即可,否則,我們就得使用連續的木板來修復多個牛棚。比如編號為3和8的木板,我們使用長度為6的木板修復,不過因為我們一開始每個牛棚一個分配了長度為1的木板,所以只需要在原來答案的基礎上加上8-3-1即可,就是4,所以說得數出它們之間隔了幾個牛棚。
  • 由上面的思路,我們得對有牛的牛棚陣列a進行排序,並使用另外一個陣列d,用來表示a中相鄰元素之間所隔著的牛棚數量
  • 先對牛棚陣列a排序,並給每一個牛棚分配一個長度為1的木板:
for (int i = 1; i <= c; i++) cin >> a[i];
//先給每個有牛的牛棚分配一塊木板,如果最大木板數量不夠用的話,就得
//將某些木板連起來了。
int ans = c;
sort(a + 1, a + 1 + c);
  • 然後,我們計算出a中相鄰元素的牛棚的數量,並對d陣列排序
	for (int i = 2; i <= c; i++) d[i - 1] = a[i] - a[i - 1] - 1;
	//d陣列總共有c-1個元素
	sort(d + 1, d + c);
  • 如果m<c的話,我們就每次選擇d陣列中最小的那個元素,因為它們之間相隔的木板數目最少,我們所需要付出的木板長度代價越低,這就是貪心思想所對應的區域性最優解。以往下去,我們使用的木板長度肯定是最少的,最終得到全域性最優解。

程式碼:

#include<iostream>
using namespace std;
#include<algorithm>
int a[205];		//作為有牛的牛棚的編號
int d[205];		//作為相鄰兩個牛棚之間間隔的牛棚的數量
int m, s, c;
int main()
{
	cin >> m >> s >> c;
	for (int i = 1; i <= c; i++) cin >> a[i];
	//先給每個有牛的牛棚分配一塊木板,如果最大木板數量不夠用的話,就得
	//將某些木板連起來了。
	int ans = c;
	sort(a + 1, a + 1 + c);
	for (int i = 2; i <= c; i++) d[i - 1] = a[i] - a[i - 1] - 1;
	sort(d + 1, d + c);
	//如果最多提供的板子數量小於牛的數量,這時候就不能每頭有牛的牛棚都分配板子了,就得將某些牛棚的板子連起來。
	if (m < c) {
		//d陣列總共有c-m個元素,因為有c頭牛,最多提供m塊板子
		//我們每次貪心的選擇相隔牛棚數最少的牛棚,這樣我們的代價越低,得到的結果越小,
		for (int i = 1; i <= c - m; i++) ans += d[i];
	}
	cout << ans << endl;
	return 0;
}

總結與思考

  • 做資訊學競賽的題目,我們最重要的能力就是快速閱讀有用資訊的能力,並建立出自己的模型,或者是畫出對應的草圖,然後在草稿紙上推演出對應的數學關係,最後用程式碼表示出來