大致题意: 你有一个工厂,初始生产力(p=1),每个单位时间你可以选择令(p)加(1)或生产(p)个商品。有(k)个任务,对于第(i)个任务你可以在第(t_i)个单位时刻减少(g_i)个商品,获得(v_i)元收入。
为什么这篇博客没有前言?因为这整篇博客都是前言。
(n)这么小怎能不想到暴枚
一开始没看数据范围习惯性以为(nle10^5)还思考了挺久的说。。。
结果鼠标往下一滑发现(nle15) emmm。。。
我想应该很容易就能想到暴力枚举(2^{15})种情况,规定完成哪些任务,然后对于每种情况再进行验证。
一时兴奋想出来又证伪掉的解方程
考虑已知要选择完成的任务,如何判断是否可以完成。
我一开始有个非常(naive)的想法,就是在每两个任务之间,先尽可能提高生产力,然后最后再死亡冲刺完成任务。
我令(p)表示初始生产力,(k)表示两个任务间的总时间,(s)为还需生产的商品(需要生产的商品减去上次剩余的商品)。
然后设(x)表示提高生产力的时间,于是就得到一个方程:
显然这是一个一元二次不等式,其中(a=1,b=p-k,c=-pk+s)。
若( riangle=b^2-4c<0),显然无解。
否则,贪心地想,令(x)取最大值(为什么要让生产力尽量大?因为社会老师讲过,看到任何问题都要想到,生产力才是根本原因),得到(x=frac{-b+sqrt{b^2-4c}}2)。
这上面的过程看似没有问题,然而,我后来猛然惊觉,你不一定要疯狂提升生产力,实际上可以预先生产商品为下一个任务做准备。
于是正解在我的脑海中一闪而过,可惜不够自信的我没能将其抓住。
最后又想了半天没有结果,无可奈何点开题解发现清一色解方程,真的就差一步啊。。。
在原先基础上稍作修改就能得到的正解
正如我证伪原先做法时所想的那样,与其疯狂提升生产力,实际上可以预先生产商品为之后的任务做准备。
那时候我就突然想到:为什么不对之后所有任务解一次方程,然后统计(min{x_{max}})作为提升的生产力呢?
然而,当时因为刚刚(Hack)掉自己,所以武断认为这个想法看看都不对劲。
最终,又一次迎来打脸,看来我和正解最终也就只差这一步之遥啊!
得出结论:我太菜了。
(上面的过程更多地是在体现我的做题经过,因此思路可能有点杂乱,具体实现详见代码。)
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 15
#define LL long long
using namespace std;
int n,T;
struct data
{
int t,g,v;I data(CI a=0,CI b=0,CI c=0):t(a),g(b),v(c){}
I bool operator < (Con data& o) Con {return t<o.t;}
}s[N+5],S[N+5];
I bool Check()
{
RI i,j,p=1,k,x,t;LL s,w=0,b,c;for(i=1;i<=T;++i)//枚举当前任务
{
for(t=S[i].t-S[i-1].t,s=-w,j=i;j<=T;++j)//t存储最多能用多少时间发展生产力,s记录需要生产的商品总和
{
if(k=S[j].t-S[i-1].t,(s+=S[j].g)<=0) continue;//若之前剩余的商品已经能完成任务,跳过
if(b=p-k,c=-1LL*p*k+s,b*b-4*c<0) return 0;//若△<0,说明无解,返回false
x=floor((-1.0*b+sqrt(b*b-4*c))/2),t>x&&(t=x);//解方程,t统计x最大值的最小值
}w+=(p+=t)*(S[i].t-S[i-1].t-t)-S[i].g;//更新生产力,更新剩余商品
}return 1;
}
int main()
{
RI i;for(scanf("%d",&n),i=1;i<=n;++i) scanf("%d%d%d",&s[i].t,&s[i].g,&s[i].v);
RI j,l=1<<n,res,ans=0;for(sort(s+1,s+n+1),i=0;i^l;++i)//枚举完成哪些任务状压下的状态
{
for(T=res=0,j=1;j<=n;++j) (i>>j-1)&1&&(res+=(S[++T]=s[j]).v);//记下需要完成的任务及总收益
res>ans&&Check()&&(ans=res);//若总收益大于当前答案,再去验证答案(一个没啥软用的小剪枝)
}return printf("%d",ans),0;
}