「 CodeForces」10E Greedy Change

Rabbit_Mua發表於2021-05-15

小兔的話

歡迎大家在評論區留言哦~


CF10E Greedy Change

題目限制

  • 時間限制:2.00s
  • 記憶體限制:250.00MB
  • 標準輸入
  • 標準輸出

題目知識點

  • 思維

題目來源

「 CodeForces」10E Greedy Change


為了方便大家閱讀通暢,題目可能略有改動,保證不會造成影響

題目

題目翻譯

給定 \(n\) 種貨幣,每種貨幣數量無限
現在要求以最少的貨幣數目表示一個正整數 \(S\)
一種方法是 DP 求一個最優解了;另一種方法是貪心:每次取當前能取的最大貨幣
現在你的任務是貪心是不一定最優的:找到最小的 \(S\),使得 貪心求出的最優解DP求出的最優解 差,或說明這樣的 \(S\) 不存在

格式

輸入格式

輸入共 \(2\) 行:
\(1\) 行:包含一個整數 \(n\)
\(2\) 行:包含 \(n\) 個整數 \(a_i\),表示每個硬幣面值

輸出格式

如果不存在 \(S\) 使得 貪心求出的最優解DP求出的最優解 差,則輸出 -1 ;否則輸出最小的 \(S\)

樣例

樣例 \(1\)

樣例輸入

5
25 10 5 2 1

樣例輸出

-1

樣例解釋

不存在 \(S\) 使得 貪心求出的最優解DP求出的最優解

樣例 \(2\)

樣例輸入

3
4 3 1

樣例輸出

6

樣例解釋

\(S = 6\) 時,貪心做法求出的最優解為 \(3 \ (4 + 1 + 1)\),DP做法求出的最優解為 \(2 \ (3 + 3)\)

提示

資料範圍

對於 \(100\%\) 的資料:滿足 \(1 \leq n \leq 400\)\(1 \leq a_i(1 \leq i \leq n) \leq 10^9\),且保證 \(a\) 陣列嚴格降序排列且 \(a_n = 1\)


思路

首先肯定不可能列舉 \(S\),因為題目沒有告訴我們 \(S\) 的上界,暴力列舉肯定會 \(TLE\)
根據資料範圍 \(1 \leq n \leq 400\)\(1 \leq a_i(1 \leq i \leq n) \leq 10^9\),可以看出應該是一個 \(O(n ^ 3)\) 或者 \(O(n ^ 2 \ \mathrm{log} \ a_i)\) 的演算法
不知道怎麼產生的 \(\mathrm{log} \ a_i\),所以考慮 \(O(n ^ 3)\) 的演算法

那麼對於一個數值 \(S\),什麼情況下 DP 比 貪心 更優呢?
假設 \(S\) 能在 \(a_{k \in [i + 1, j]}\) 中取完後沒有剩餘(最少取了 \(p\) 次),而貪心取完了 \(a_i\) 後只能取 \(a_{k \in[j + 1, n]}\) 了(包括取 \(a_{k \in[j + 1, n]}\) 一共取了 \(q\) 次);若 \(p < q\),則貪心不是最優的
因此我們可以列舉區間 \([i, j]\),如果滿足 \(p < q\),則找到了一個解 \(S\);最後在所有的 \(S\) 中取最小的即可


分析

知道了大概思路,如何求 \(p\)\(q\) 呢?

我們要保證 \(S\) 能在 \(a_{k \in [i + 1, j]}\) 中取完 並且 貪心取完了 \(a_i\) 後只能取 \(a_{k \in[j + 1, n]}\)

  • 先保證前者:先選一個數 \(A\),記 \(A\)\(a_{k \in [i + 1, j]}\) 中取完之後剩下 \(B\),那麼 \(S\) 就可以是 \(A - B\)\(A\) 如何選就要看後者了)
  • 再保證後者:因為 \(B\) 是取完後剩下的,所以 \(B < a_j\);要保證 \(S > a_i\)\(S \ \mathrm{mod} \ a_i < a_j\),不妨假設 \(S\) 只取了 \(1\)\(a_i\)(因為要保證當前 \(S\) 也最小嘛),所以 \(S > a_i\)\(S - a_i < a_j\);因為 \(B < a_j\),則 \(a_j - B \geq 1\);因此可以選一個 \(A < a_i\),使得 \(A + a_j - B \geq a_i\),能保證這 \(2\) 個式子的只有 \(A = a_i - 1\) 了;得到 \(A\) 後,\(B\)\(S\) 也就出來了

對每個區間 \([i, j]\),按照上面的方法計算 \(p, q\) 就可以了
如果所有區間都不存在 \(p < q\) 就輸出 \(-1\)


\(\mathrm{code}\)

#include <cstdio>

int Max(int a, int b) { return (a > b) ? a : b; }
int Min(int a, int b) { return (a < b) ? a : b; }

int rint()
{
	int x = 0, fx = 1; char c = getchar();
	while (c < '0' || c > '9') { fx ^= (c == '-' ? 1 : 0); c = getchar(); }
	while ('0' <= c && c <= '9') { x = (x << 3) + (x << 1) + (c ^ 48); c = getchar(); }
	if (!fx) return -x;
	return x;
}

const int MAX_n = 400;

int n, res = 2e9;
int v[MAX_n + 5];

int main()
{
	n = rint();
	for (int i = 1; i <= n; i++)
		v[i] = rint();
	bool ok = false;
	for (int i = 1; i <= n; i++)
	{
		for (int j = i + 1; j <= n; j++)
		{
			int	dp = 0, count = 0, now = v[i] - 1; // A = v[i] - 1 
			for (int k = i + 1; k <= j; k++)
				dp += now / v[k], now %= v[k]; // 求出 B 
			int temp = now = v[i] - 1 - now + v[j]; // 計算出 S, 備份一下 
			for (int k = 1; k <= n; k++)
				count += now / v[k], now %= v[k];
			if (dp + 1 < count) res = Min(res, temp), ok = true;
			// dp + 1 是因為之前多加了一個 v[j] 
			// 若 p < q, 則 當前S 可能是答案 
		}
	}
	printf("%d\n", ok ? res : -1);
	return 0;
}


相關文章