题目链接:

https://codeforces.com/problemset/problem/1703/G

题意:

\(n\) 个箱子需要按顺序打开,第 \(i\) 个箱子中有 \(a_i\) 个金币,可以用好钥匙和坏钥匙去开箱子,好钥匙需要花费 \(k\) 个金币,坏钥匙不花费金币,但是会使当前这个箱子开始到第 \(n\) 个箱子中的金币全部减半。开箱的过程中金币可以是负数的,即可以赊账,问最多能留下多少硬币。

思路:

对于第 \(i\) 个箱子,选择哪个钥匙取决于哪个钥匙的花费少,当坏钥匙的花费小于好钥匙的花费的时候,后面所有的箱子都会采用坏钥匙去开。
因为当前用了坏钥匙,后面的箱子的金币减半,那么坏钥匙的花费就更小了,所以整个开箱的过程分为两段,前面是好钥匙,后面是坏钥匙。
对于坏钥匙而言,最多用 32 次,后面所有的箱子就没有金币了。所以可以采用暴力的做法,枚举使用坏钥匙的第一个箱子,去求出最大的剩余金币。

#include <bits/stdc++.h>
using namespace std;
#define LL long long
void solve(){
	LL n, k;
	cin >> n >> k;
	vector <LL> a(n + 1), s(n + 1), mi(40);
	for (int i = 1; i <= n; i ++ ){
		cin >> a[i];
		s[i] = s[i - 1] + a[i];
	}
	mi[1] = 2;
	for (int i = 2; i <= 32; i ++ )
		mi[i] = mi[i - 1] * 2;
	LL ans = 0;
	for (int i = 0; i <= n; i ++ ){
		LL sum = s[i] - i * k;
		for (int j = i + 1; j <= min(i + 32LL, n); j ++ ){
			sum += a[j] / mi[j - i];
		}
		ans = max(ans, sum);
	}
	cout << ans << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL T;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}

坏钥匙最多 32 个,所以也可以采用 \(dp\),定义 \(dp[i][j]\) 为前 \(i\) 个箱子用了 \(j\) 个坏钥匙所获得的最大的收益。

#include <bits/stdc++.h>
using namespace std;
#define LL long long
const LL INF = 1e18;
void solve(){
	LL n, k;
	cin >> n >> k;
	vector <LL> a(n + 1);
	vector < vector<LL> > dp(n + 1, vector<LL> (40, -INF) );
	for (int i = 1; i <= n; i ++ )
		cin >> a[i];
	dp[0][0] = 0;
	for (int i = 1; i <= n; i ++ ){
		for (int j = 0; j <= 33; j ++ ){
			dp[i][j] = max(dp[i][j], dp[i - 1][j] + (a[i] >> j) - k);  //使用好钥匙
			if (j)
				dp[i][j] = max(dp[i][j], dp[i - 1][j - 1] + (a[i] >> j) );  //使用坏钥匙
		}
		dp[i][33] = max(dp[i][33], dp[i - 1][33]);  //注意更新使用了所有坏钥匙的情况
	}
	LL ans = -INF;
	for (int j = 0; j <= 33; j ++ )
		ans = max(ans, dp[n][j]);
	cout << ans << "\n";
}
int main(){
	ios::sync_with_stdio(false);cin.tie(0);
	LL T;
	cin >> T;
	while(T -- ){
		solve();
	}
	return 0;
}
posted on 2022-07-13 15:06  Hamine  阅读(97)  评论(0编辑  收藏  举报