【动态规划】JZOJ_4245 er

题意

n n n个东西 ( n &lt; = 2 ) (n&lt;=2) (n<=2),我们有 k k k种符文可以改变某个东西的价值。

有三种符文形式如下:
1、赋值强化符文,直接把一个东西赋值为一个常数。
2、加法强化符文,把一个东西加上一个常数。
3、乘法强化符文,把一个东西乘上一个常数。

最多能用 k k k个符文,求出按某种顺序用某些符文后这 n n n个东西的乘积的自然对数最大。

思路

首先有 l o g e ( a b ) log_e(ab) loge(ab)= l o g e ( a ) + l o g e ( b ) log_e(a)+log_e(b) loge(a)+loge(b)
证明:
不妨设 l o g e ( a b ) = x 、 l o g e ( a ) = p 、 l o g e ( b ) = q log_e(ab)=x、log_e(a)=p、log_e(b)=q loge(ab)=xloge(a)=ploge(b)=q
那么 e x = a b 、 e p = a 、 e q = b e^x=ab、e^p=a、e^q=b ex=abep=aeq=b
又因为 e p ∗ e q = a b 、 e p + q = a b e^p*e^q=ab、e^{p+q}=ab epeq=abep+q=ab
所以 p + q = x p+q=x p+q=x

根据这个,我们就可以分别计算加法和乘法的答案,至于赋值强化符文,我们可以暴力去判断。
n = 1 n=1 n=1时,我们可以用随便搞搞,下面是 n = 2 n=2 n=2的情况。
f i , j f_{i,j} fi,j表示用前 i i i个加法强化符文,第一个东西的价值为 j j j的两个价值的最大乘积。
两种转移,表示加给第一个东西或加给第二个东西,我们用前缀和记录加法符文的和,然后就通过第一个东西求出第二个东西的值了。
最后枚举一下选乘法符文的情况,计算一下最大答案。

代码

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>

int n, m, k, p1, p2, type1, lenAdd, lenMul;
int add[101], mul[101], sum[101], maxadd[101];
int f[101][200001];
double ans;
double sumMul[101];

int cmp(int x, int y) {
	return x > y;
}

int main() {
	scanf("%d %d %d", &n, &m, &k);
	scanf("%d", &p1);
	if (n == 2) scanf("%d", &p2);
	for (int i = 1, op; i <= m; i++) {
		scanf("%d", &op);
		if (op == 1) scanf("%d", &type1);
		else if (op == 2) scanf("%d", &add[++lenAdd]);
		else scanf("%d", &mul[++lenMul]);
	}
	std::sort(add + 1, add + lenAdd + 1, cmp);
	std::sort(mul + 1, mul + lenMul + 1, cmp);
	sum[0] = p1 + p2;
	for (int i = 1; i <= k; i++) {
		sum[i] = sum[i - 1] + add[i];
		sumMul[i] = sumMul[i - 1] + log(mul[i]);
	}
	if (n == 1) {
		for (int i = 0; i <= k; i++) {
			ans = std::max(ans, log(sum[std::min(i, lenAdd)]) + sumMul[std::min(k - i, lenMul)]);
			if (i != k && type1)
				ans = std::max(ans, log(type1 + sum[std::min(i, lenAdd)] - p1) + sumMul[std::min(k - i - 1, lenMul)]);
		}
		printf("%.3lf", ans);
		return 0;
	}
	memset(f, 0xcf, sizeof(f));
	f[0][p1] = p1 * p2;
	for (int i = 0; i <= lenAdd; i++)
		for (int j = p1; j <= sum[i] - p2; j++) {
			f[i + 1][j + add[i + 1]] = std::max(f[i + 1][j + add[i + 1]], f[i][j] + add[i + 1] * (sum[i] - j));
			f[i + 1][j] = std::max(f[i + 1][j], f[i][j] + add[i + 1] * j);
			maxadd[i] = std::max(maxadd[i], f[i][j]);
	 }
	for (int i = 0; i <= k; i++)
		ans = std::max(ans, log(maxadd[std::min(lenAdd, i)]) + sumMul[std::min(k - i, lenMul)]);
	if (type1) {
		k--;
		sum[0] = type1 + p2;
		for (int i = 1; i <= k; i++)
			sum[i] = sum[i - 1] + add[i];
		memset(f, 0xcf, sizeof(f));
		memset(maxadd, 0, sizeof(maxadd));
		f[0][type1] = type1 * p2;
		for (int i = 0; i <= k; i++)
			for (int j = type1; j <= sum[i] - p2; j++) {
				f[i + 1][j + add[i + 1]] = std::max(f[i + 1][j + add[i + 1]], f[i][j] + add[i + 1] * (sum[i] - j));
				f[i + 1][j] = std::max(f[i + 1][j], f[i][j] + add[i + 1] * j);
				maxadd[i] = std::max(maxadd[i], f[i][j]);
			}		
		for (int i = 0; i <= k; i++)
			ans = std::max(ans, log(maxadd[std::min(lenAdd, i)]) + sumMul[std::min(k - i, lenMul)]);
		sum[0] = p1 + type1;
		for (int i = 1; i <= k; i++)
			sum[i] = sum[i - 1] + add[i];
		memset(f, 0xcf, sizeof(f));
		memset(maxadd, 0, sizeof(maxadd));
		f[0][p1] = p1 * type1;
		for (int i = 0; i <= k; i++)
			for (int j = p1; j <= sum[i] - type1; j++) {
				f[i + 1][j + add[i + 1]] = std::max(f[i + 1][j + add[i + 1]], f[i][j] + add[i + 1] * (sum[i] - j));
				f[i + 1][j] = std::max(f[i + 1][j], f[i][j] + add[i + 1] * j);
				maxadd[i] = std::max(maxadd[i], f[i][j]);
			}		
		for (int i = 0; i <= k; i++)
			ans = std::max(ans, log(maxadd[std::min(lenAdd, i)]) + sumMul[std::min(k - i, lenMul)]);
	}
	printf("%.3lf", ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值