1. 程式人生 > 其它 >P8109 [Cnoi2021]幻想鄉程式設計大賽 題解

P8109 [Cnoi2021]幻想鄉程式設計大賽 題解

原題連結

簡要題意:

給定兩個有序數列 \(\{a_n\} \space \{b_n\}\). 對於所有排列 \(p_i\),求 \(\sum_{i = 1}^n \min(a_i , b_{p_i})\) 的最大值。

另一種解釋方式:給定兩個有序數列 \(\{a_n\} \space \{b_n\}\). 求一種 \(a \rightarrow b\) 的對映,使得每組對映的最小值的和最大。求這個最大值。

\(n \leq 10^5\).

貪心經典題。

我們可以想到一種顯然的匹配方式:即 \(p_i = i\),保持原排序結果不變。那麼這樣是否最優呢?我們可以做一個簡單的證明。

只要從小資料出發。考慮對於 \(a_i < a_j\)

\(b_i < b_j\),這四個數如何匹配?

兩種方式:\(\min(a_i , b_i) + \min(a_j , b_j)\),或者 \(\min(a_i , b_j) + \min(a_j , b_i)\). 記前者為 \(S\),後者為 \(T\).

我們試圖證明總有 \(S \geq T\).

如果 \(a_i < a_j < b_i < b_j\),則 \(S = a_i + a_j , T = a_i + a_j\),有 \(S = T\).

如果 \(a_i < b_i < a_j < b_j\),則 \(S = a_i + a_j , T = a_i + b_i\)

,有 \(S > T\).

如果 \(b_i < a_i < a_j < b_j\),則 \(S = b_i + a_j , T = b_i + a_i\),有 \(S > T\).

如果 \(a_i < b_i < b_j < a_j\),則 \(S = a_i + b_j , T = a_i + b_i\),有 \(S > T\).

如果 \(b_i < a_i < b_j < a_j\),則 \(S = b_i + b_j , T = b_i + a_i\),有 \(S > T\).

如果 \(b_i < b_j < a_i < a_j\)

,則 \(S = b_i + b_j , T = b_i + b_j\),有 \(S = T\).

當然如果存在等號,那麼和小於號可以一樣得出結論。

最後我們就得到了 \(S \geq T\). 那麼也就是說:

對於任意的 \(i < j\),必有 \((a_i , b_i)(a_j , b_j)\) 的匹配方式比 \((a_i , b_j)(a_j , b_i)\) 不劣。

所以我們只需要把 \(a,b\) 從小到大排序,然後讓 \(p_i = i\),掃一遍即可。

而注意到原題已經幫我們排好序了(其實這也是一點有力的暗示),那麼我們直接掃一遍就行了。

時間複雜度:\(\mathcal{O}(n + m)\).

#include <bits/stdc++.h>
using namespace std;

const int N = 1e5 + 1;

int Ans = 0, a[N];

int main() {
	int n; scanf("%d",&n);
	for(int i = 1; i <= n; i++) scanf("%d",a + i);
	for(int i = 1; i <= n; i++) {
		int x; scanf("%d",&x);
		Ans += min(x , a[i]);
	}
	printf("%d\n",Ans);
}
簡易的程式碼勝過複雜的說教。