版权声明:虽然本蒟蒻很菜,但各位dalao转载请注明出处谢谢。 https://blog.csdn.net/xuxiayang/article/details/86689816
有 件装备,有 个升级卡,分为赋值、增值、乘值三种,现要从中选择 个升级卡,使得装备的战斗力之和最大
对于20% 的数据,
;
对于全部数据
,最多一张赋值卡。
输入数据中所有数不超过2000。
贪心好题
对于
的情况,直接分成使用赋值卡和不使用赋值卡的方式
对于一些增值和乘值卡,显然是先使用增值再乘值更优,所以我们可以暴力给那件装备增值的值,然后再用乘值卡乘,最后计算即可。加上前缀和优化后时间复杂度:
对于
的情况,分成给1使用赋值卡,给2使用赋值卡和不使用赋值卡三种
这时因为最后的答案是所有装备值的乘积,所以乘值卡给谁并不重要,主要是增值的分配问题。
注意到我们有
张卡,从中选出任意张总和不超过
的卡,求方案数
这其实就是0/1背包问题
因此,我们用0/1背包求出能否用现在的增值卡拼凑出这个值,既然能算出分配给1的值,则用总值减去分配给1的值即为分配给2的值,这时我们就可以
转移啦,加上前缀和优化后复杂度为
#include<cmath>
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
using namespace std;int n,m,k,a,b,fz,jf[101],cf[101],ljf,lcf,opt,c,nowa,nowb,f[2000001];
long long sjf[101];
double now,ans,scf[101];
inline char Getchar()
{
static char buf[10000000],*p1=buf+10000000,*pend=buf+10000000;
if(p1==pend)
{
p1=buf; pend=buf+fread(buf,1,10000000,stdin);
if (pend==p1) return -1;
}
return *p1++;
}
inline long long read()
{
char c;int d=1;long long f=0;
while(c=Getchar(),!isdigit(c))if(c==45)d=-1;f=(f<<3)+(f<<1)+c-48;
while(c=Getchar(),isdigit(c)) f=(f<<3)+(f<<1)+c-48;
return d*f;
}
inline bool cmp(int x,int y){return x>y;}
signed main()
{
freopen("1.txt","r",stdin);
n=read();m=read();k=read();
a=read();
if(n==1)//分类讨论
{
for(register int i=1;i<=m;i++)
{
opt=read();c=read();
if(opt==1) fz=c;
if(opt==2) jf[++ljf]=c;
if(opt==3) cf[++lcf]=c;
}
sort(jf+1,jf+1+ljf,cmp);
sort(cf+1,cf+1+lcf,cmp);//肯定是越大越优了啦
for(register int i=1;i<=ljf;i++) sjf[i]=sjf[i-1]+jf[i];
scf[1]=log(cf[1]);
for(register int i=2;i<=lcf;i++) scf[i]=scf[i-1]+log(cf[i]);//前缀和计算,用到一个公式log(ab)=log(a)+log(b)
if(fz>a)//此时赋值才可能是更优的,否则就是浪费
{
now=0;
for(register int i=0;i<min(ljf,k);i++)
{
nowa=fz+sjf[i];
now=max(now,log(nowa)+scf[min(k-1-i,lcf)]);//前面的加,后面的乘
}
ans=now;
}
now=0;
for(register int i=0;i<=min(ljf,k);i++)//注意循环的上界不一样,因为我上面已经使用了一张卡所以是小于,而这里还没用所以是不大于
{
nowa=a+sjf[i];
now=max(now,log(nowa)+scf[min(k-i,lcf)]);//同样的因为这里没有多用一张卡所以不用再-1
}
ans=max(ans,now);
printf("%.3lf\n",ans);
}
else
{
b=read();
for(register int i=1;i<=m;i++)
{
opt=read();c=read();
if(opt==1) fz=c;
if(opt==2) jf[++ljf]=c;
if(opt==3) cf[++lcf]=c;
}
sort(jf+1,jf+1+ljf,cmp);
sort(cf+1,cf+1+lcf,cmp);
for(register int i=1;i<=ljf;i++) sjf[i]=sjf[i-1]+jf[i];
scf[1]=log(cf[1]);
for(register int i=2;i<=lcf;i++) scf[i]=scf[i-1]+log(cf[i]);//以上部分和上面一样
if(fz)
{
memset(f,0,sizeof(f));
f[0]=1;
for(register int i=0;i<min(ljf,k);i++)
{
for(register int j=sjf[ljf];j>=jf[i];j--) f[j]|=f[j-jf[i]];//背包计算当前方案是否合法
for(register int j=0;j<=sjf[i];j++)
if(f[j])//如果合法
{
nowa=fz+j;nowb=b+sjf[i]-j;//j点分给a,其它的分给b
now=max(now,log(nowa)+log(nowb)+scf[min(k-i-1,lcf)]);//因为用了赋值卡,所以要-1
nowa=a+j;nowb=fz+sjf[i]-j;//反之
now=max(now,log(nowa)+log(nowb)+scf[min(k-i-1,lcf)]);
}
}
ans=max(ans,now);
}
memset(f,0,sizeof(f));
f[0]=1;
for(register int i=0;i<=min(ljf,k);i++)
{
for(register int j=sjf[ljf];j>=jf[i];j--) f[j]|=f[j-jf[i]];
for(register int j=0;j<=sjf[i];j++)//同理
if(f[j])
{
nowa=a+j;nowb=b+sjf[i]-j;
now=max(now,log(nowa)+log(nowb)+scf[min(k-i,lcf)]);//因为没用赋值卡,所以不用减一
}
}
ans=max(ans,now);
printf("%0.3lf",ans);
}
}