zoukankan      html  css  js  c++  java
  • 【BZOJ2667】[CQOI2012] 模拟工厂(暴力枚举+贪心验证)

    点此看题面

    大致题意: 你有一个工厂,初始生产力(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)表示提高生产力的时间,于是就得到一个方程:

    [(p+x)(k-x)>sRightarrow x^2+(p-k)x-pk+s<0 ]

    显然这是一个一元二次不等式,其中(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;
    }
    
  • 相关阅读:
    Vue-基础(四)
    Vue-基础(三)
    Vue-基础(一)
    Vue-基础(二)
    CSS-初始化模板2(common.css)
    CSS-初始化模板1(normalize.css)
    CSS预处理器-Less
    MySQL视窗函数row_number(), rank(), denser_rank()
    LeetCode第4题:寻找两个有序数组的中位数
    无重复字符的最长子串
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ2667.html
Copyright © 2011-2022 走看看