D小红升装备

思路:

1、n和x都很小,我们考虑用dp。

2、其实有点类似于完全背包问题,装备代表每个物品,金币是体积,战力是价值,最多升到k级,代表每个物品最多选择k次。

3、状态表示:dp[i][j]表示我们从前i个装备里面选,花费的金币不超过j的方案的战力的集合; 属性值:最大值

4、状态转移:

分两类:(1) 不选第i个装备 : dp[i][j] = dp[i-1][j]
		(2) 选第i个装备,选择之后还要分为k+1类,即装备升0--k级
			当装备升k级的时候,dp[i][j] = dp[i-1][j - y - k*p] + power;
            分析一下,第i个装备选了并且升了k级,花费的金币为 y + k * p , 所以上一个的状态为	
            dp[i-1][j - y - k * p]----> dp[i][j] = dp[i-1][j - y - k*p] + power(第i个装备升k级获得的战力);

5、注意数据为1e9 很敏感我们用long long , 不然会爆int

#include<bits/stdc++.h>
using namespace std;
const int N = 310;
typedef long long LL;
LL dp[N][N];
LL a[N][5];
/*
3 10
10 3 5 2 100
100 20 5 4 100
20 2 1 10 1
*/
int main()
{
	int n, m;
	cin >> n >> m;
	for(int i = 1; i <= n; i ++)
	{
		cin >> a[i][0] >> a[i][1] >> a[i][2] >> a[i][3] >> a[i][4];
	}
	for(int i = 1; i <= n; i++)
	{
		//别代表装备初始战力,购买该装备需要的金币、升1级花费的金币、升1级提升的战力、最高可以提升的等级。
		LL x, y, z, p, l;
		x = a[i][0], y = a[i][1], z = a[i][2], p = a[i][3], l = a[i][4];
		
		for(int j = 0; j <= m; j ++)
		{
			// 不购买第i个装备 
			
			dp[i][j] = dp[i-1][j];
			//购买第i个装备
 
			if(j >= y)
			{
				for(int k = 0; k <= l && j - y - k * z >= 0; k ++)
				{
					dp[i][j] = max(dp[i][j], dp[i-1][j - y - k * z] + x + (LL)k * p);
				}
			}
		}
	}
	cout << dp[n][m] <<endl;
	return 0;
}

E 矩阵的划分

思路

1、思维题:要直知道一些结论,不然就需要猜测。

2、首先我们想一想怎么样可以让分数最大化,两种摆放的方法,如果说两种的得分不一样,我们肯定优先考虑得分高的方案,并且将它摆满,如果可以摆满这肯定是最优的方案。

3、如果单独的一种不能摆满,我们就要考虑两者混合的情况。

4、我们考虑怎么样可以尽可能的摆满,首先题目保证n是偶数,所以说2 * 2 的摆法一定可以摆满;关键是L型的如何考虑。

5、我们考虑一下L型的摆放情况:

(1)如果n是3 的倍数,全部摆放L ,不可能只剩一个格子,换句话说,就是剩余的格子数大于1。(应为 L型的只能凑出来2、3)

(2)如果n不是3的倍数,全部摆放L,一定有一个方案使得最后恰好只剩一个格子。

6、综合以上的情况,我们可以得出以下结论:

(1)当n是3的倍数,并且加上题目的条件,n是6的倍数,这时候两种方案都可以填满,所以 res = max(n * n /3 * x,n * n / 4 * y )

(2) 当n不是3 的倍数的时候,我们要加上两者混合的情况,因为填2 * 2 的一定可以填满,所以混合的情况就出现在填L剩一个格子的时候,我们可以把这一个L加上空格换成2 * 2 的摆放。此时就会有三种情况了,我们取一个最大值。

  • 尽可能摆放L型的:我们知道它最少可以剩一个格子,所以结果的数量是(n * n - 1)/ 3 * x
  • 两者混合的时候,就是L尽可能摆满减掉最后替换的格子,(n * n - 1)/ 3 - x + y
  • 尽可能摆满2 * 2 的,一定可以摆满(n * n)/ 4 * y
  • res = max((n * n - 1)/ 3 * x, (n * n - 1)/ 3 - x + y, (n * n)/ 4 * y)

7、有点别扭的地方,就是说我们在混合的时候为什么不能一半的L和一半的田,其实你想一下,如果两者的价值不一样,我们肯定都摆价值大的最后的结果更大。

但是还应该考虑的问题是L型的摆法摆放的个数肯定会比田字形摆放的多。这点还没想明白!!!

我们可以这样想,在相同的面积之下,不管摆放的个数的多少,我们是求得和的最大值,即 个数 * 每个价值 的最大值,只考虑一个方面是不行的。

F 小红拿宝箱

思路

1、首先大体上分为两大类:(1)抽到的第一个宝箱不拿,直接消耗完不拿的次数,然后拿取的方案就是在所有的宝箱里里面任意选两个,求期望;res = (ai + aj)/C(n,2); (2)抽到的第一个宝箱我们拿,接下来我们就考虑剩下的n-1个宝箱,再拿一个并且还有一次“不拿的机会”。

2、我们着重考虑一下第二种方案。

3、我们第一次拿了a[i] , 第二次就是从剩下的n-1 个里面再拿一个,假设我们第二次拿的是a[j], 因为我们还有一次不拿的机会,所以我们可以选择拿或不拿:

(1)我们不拿a[j], 那么第二个宝箱的期望就是n-1个宝箱里面任取一个的期望:avge1 = (sum - a[i])/(n-1);

(2)我们拿a[j],但是我们要看一下a[j]和平均值的大小关系,如果a[j] 小于平均值,我们干脆不拿,a[j]的期望==avge1;如果a[j]的值大于均值我们拿a[j],因为这样的期望会更大;所以对与每个a[i] : avge_j += max(avge1, a[j])(j:0-n), avge_j = avge_j / n-1, res_i = avge_j + a[i];

4、当我们求a[j]的期望的时候还要再遍历一边数组,复杂度是O(n * n)的,我们考虑优化一下。avge_j其实就是:大于平均数的和加上不于平均数的数取avge1的和,前者我们可以用后缀和来求,从而优化掉一层for()循环

5、最后的res 我们求得其实是每种选取方案期望的和,我们还要除以n

 #include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
double a[N], sum[N];
int main()
{
   int n;
   cin >> n;
   for(int i = 0; i < n; i ++)
   {
       cin >> a[i];
   }
   sort(a, a + n);
   for(int i = n - 1;  i >= 0; i --)
   {
       sum[i] = sum[i+1] + a[i];//后缀和
   }
   if(n <= 2)
   {
       cout << sum[0] << endl;
       return 0;
   }
   //第一次拿箱子之后不选这个箱子,之后没有不拿的机会了,所以就选两个算期望
   double avge = sum[0] * 2 / n;
   //cout << "avge:" <<avge << endl;
   //第一次拿了一个后计算期望,与不拿的时候比较期望
   //f[i]:对第i个数而言,如果拿了它,在剩下的n-1个数中,还有一次“不拿”的机会,最终期望的最大值
   //res = max(f[i] + a[i], avge)
   double res = 0;
   for(int i = 0; i < n; i ++)
   {
       //如果说,第二次选的箱子也不拿,那么剩下n-1的个箱子的期望就是平均值了
       double avge1 = (sum[0] - a[i]) / (n - 1);
       //cout << avge1 << endl;
       //批量处理第二个箱子,暴力的话会超时
       int id = lower_bound(a , a + n , avge1) - a;//找到第一个大于等于均值的数的下标
       //cout << "id: " <<id <<endl;
       double E = sum[id] + avge1 * id;
       if(a[i] >= avge1) E -= a[i];
       else E -= avge1;
       E = E / (n - 1);
       res += max(avge, E + a[i]); 
   }
   // res 是所有可能取法期望的总和,所以最后要除以n
   printf("%.7lf", res / n);
   return 0;
}