考试

【问题描述】

小 S 要参见一场考试,这场考试一共有 k 道题目。每道题目有分值 ??,难度 zi 和
类型 si。这些题目一共有 m 中不同的类型。
由于小 S 偏科严重,所以对不同类型的题目熟练度可能不同,在第 i 种类型的题
目熟练度为 yi。
为了简化问题,我们认为当小 S 以 y 的熟练度做难度为 z 分值为 a 的题目时,
会获得 a ⋅ (1 − max (0,1 − ??)2) 的分数。
众所周知,做题不顺利可能影响心态和发挥,在这里我们认为,如果小 S 在某一
道题目的得分低于总分的 64%,那么接下来做题熟练度会下降。具体来说,接下来的
第 i 道题熟练度会下降 ci%。每一道题只会受到之前最后一次分数低于 64% 的影响。
(就是说如果第 t 道题的分数小于满分的 64%,那么第 t+i 道题的熟练度会下降 Ci%。
同时,如果有多道题分数小于满分的 64%,只有最后一次会有影响。比如说第 1 道题分
数小于 64%那么第 3(即 1+2)题会下降 C2%,接下来第 4 题又低于 64%那么第 5(即 4+1)
题会下降 C1%。)
根据目前的描述,已经可以确定出小 S 在每道题的得分了。但是小 S 有 n 瓶神
奇的饮料,在做题时喝掉第 i 瓶饮料可以让这道题的熟练度提高 xi%。每瓶饮料只能
在一道题喝,做一道题时可以喝多瓶饮料。
熟练度的下降和上升是依次进行,每次都按当前的百分比计算。简单来说,就是在
原来的熟练度上乘 1 ± u%。
现在问小 S 的总分最高是多少。

【输入格式】

从输入文件 test.in 中读入数据。
第一行三个整数n, m, k,分别表示饮料的瓶数、题目类型的数量和试题的数量。
第二行 n 个整数 x1, x2, ⋯ , ??,表示饮料能带来的提升
第三行 m 个整数 y1, ?2, ⋯ , ??,表示每种题目类型的熟练度。
第四行 k − 1 个整数 c1, c2, ⋯ , ck−1,表示做题不顺利对之后熟练度的下降程度。
接下来 k 行,每行三个整数 ai, si, zi,分别表示题目的分值、类型和难度。

【输出格式】

输出到文件 test.out 中。
输出一行一个实数,表示答案。四舍五入后保留两位小数输出。

【样例 1 输入】

1 1 2
10
100
50
100 1 260
100 1 200

【样例 1 输出】

2019 年非专业级软件能力认证提高级(第二轮) 第二次认证 考试(test) 第 3 页 共 8 页
141.72

【样例 1 解释】

一共一瓶饮料,可以选择在做第一题或第二题时喝。
如果在做第二题时喝:
做第一题的能力为 100,得分为 100 × (1 − (1 −
100
260)2) = 62 22
169。
由于分数低于 64,所以第二题的能力会下降 50%,又由于饮料可以提高 10%,
所以实际能力为 100 × 50% × 110% = 55。
于是第二题的得分为 100 × (1 − (1 −
55
200)2) = 47 7
16。
总得分为 109 1535
2704 分。
可以类似计算出在选择在做第一题时喝的得分为 141 121
169
分,所以最高分为
141 121
169 分。


思路

  • n==0时,暴力
  • n!=0时,状压DP
  • f[i][j][S]表示到第i题、上一次挂在第j题、喝的饮料状态为S的最大答案

代码

#include <iostream>
#include <cstdio>
#include <iomanip>
#include <cstring>
using namespace std;
int n,m,k,y[12];
double x[12],c[500005],f[105][105][1030],xx[1030];
struct fdfdfd{double a,z;int s;}e[500005];
double gread(double y,double z,double a){
	double t=max(0.0,1-y/z);
	return a*(1-t*t);
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);//饮料的瓶数、题目类型的数量、试题的数量 
	for(int i=1;i<=n;++i) scanf("%lf",&x[i]),x[i]=(100+x[i])/100;//饮料能带来的提升 
	for(int i=1;i<=m;++i) scanf("%d",&y[i]);//每种题目类型的熟练度 
	for(int i=1;i<k;++i) scanf("%lf",&c[i]),c[i]=(100-c[i])/100;//做题不顺利对之后熟练度的下降程度 
	for(int i=1;i<=k;++i) scanf("%lf%d%lf",&e[i].a,&e[i].s,&e[i].z);//题目的分值、类型和难度 
	if(n==0)
	{
		double ans=0;
		for(int i=1,lastf=0;i<=k;++i)
		{
			double p=y[e[i].s]*(lastf?c[i-lastf]:1),soc=gread(p,e[i].z,e[i].a);
			if(e[i].a*0.64>soc) lastf=i;
			ans+=soc;
		}
		cout<<fixed<<setprecision(2)<<ans<<'
';
		return 0;
	}
	xx[0]=1;
	for(int i=1;i<1<<n;++i)
		for(int j=0;j<n;++j)
			if(i&1<<j) xx[i]=xx[i^1<<j]*x[j+1];//xx[a]表示喝饮料状态为a时,熟练度提高值
	for(int i=1;i<=k;++i)
		for(int j=0;j<=i;++j)
			for(int s=0;s<1<<n;++s)f[i][j][s]=-1;
	f[0][0][0]=0;
	for(int i=1;i<=k;++i)
		for(int j=0;j<i;++j)
			for(int s=0;s<1<<n;++s)
				for(int s2=s;;s2=(s2-1)&s)//枚举子集
				{
					if(f[i-1][j][s2]==-1)
					{
						if(!s2) break;
						continue;
					}
					double p=y[e[i].s]*(!j?1:c[i-j])*xx[s2^s],soc=gread(p,e[i].z,e[i].a);
					if(e[i].a*0.64>soc) f[i][i][s]=max(f[i][i][s],f[i-1][j][s2]+soc);
					else f[i][j][s]=max(f[i][j][s],f[i-1][j][s2]+soc);
					if(!s2) break;
				}
	double ans=0;
	for(int i=0;i<=k;++i) ans=max(ans,f[k][i][(1<<n)-1]);
	cout<<fixed<<setprecision(2)<<ans<<'
';
	return 0;
}
原文地址:https://www.cnblogs.com/wuwendongxi/p/13915687.html