1. 程式人生 > >[CQOI2012]模擬工廠 題解(搜尋+貪心)

[CQOI2012]模擬工廠 題解(搜尋+貪心)

[CQOI2012]模擬工廠 題解(搜尋+貪心)

標籤:題解
閱讀體驗:https://zybuluo.com/Junlier/note/1327574

連結題目地址:洛谷P3161 BZOJ P2667
這個題練一練綜合思想還是不錯的。。。(然而蒟蒻不會啊)

做法

肯定是在能完成某些訂單的情況下使自己生產力越高越好是吧(一個大致的貪心方向)
但是我們不知道自己到底應該怎麼去決定提高生產力時間
那麼換個角度,不從時間來看,從訂單上來看

貪心

我們假設一定要完成訂單\(1~n\)
那麼應該如何貪心選時間提升生產力呢,當然是在能滿足所有訂單的基礎上儘量多地提高生產力
那麼對於訂單\(i\)\(j\)

,我們都會得到方程:(設\(pdc(produce)\)為完成訂單\(i\)時的生產力,\(T\)為距離\(j\)訂單的時間,\(x\)為用來提升生產力的時間,\(gv(give)\)是訂單\(j\)需求量)
\[(pdc+x)×(T-x)=gv\]對所有我們一定要完成的訂單一個一個完成,每次完成一個訂單時對它之後的每一個訂單我們都解這麼一個方程,得到儘可能的休息時間,那麼這樣子一定是對的吧

然後可以想到

上面是\(1~n\)我們都想完成,現在不同了,我們可以放棄一些訂單
再看資料範圍:\(n<=15\)?,那不就暴力列舉狀態選還是不選啊
然後對於上面那個方程,如果無解\(△ < 0\)

肯定這種計劃是不行的
然後直接用求根公式會得到:\[\frac{T-pdc+\sqrt{(pdc+T)^2-4×gv}}{2}\]算一下時間複雜度:\(O(2^n×n^2)\)很對呀,那就做完了
列舉狀態雖可以直接列舉,但也可以搜是吧,那我們就叫他搜尋了

給出程式碼

哼哼~壓行是看程式碼人的噩夢,是寫程式碼者的美夢(雖然筆者只稍稍壓行了。。。

#include<bits/stdc++.h>
#define il inline
#define rg register
#define ldb double
#define lst long long
#define rgt register int
#define N 20
#define M 100050
using namespace std;
const int Inf=1e9;
il lst MAX(rg lst x,rg lst y){return x>y?x:y;}
il lst MIN(rg lst x,rg lst y){return x<y?x:y;}
il int read()
{
    int s=0,m=0;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')m=1;ch=getchar();}
    while( isdigit(ch))s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
    return m?-s:s;
}

int n,UP;
lst Ans,Res;
int sgn[N],top;
struct DD{lst tt,gv,gt;}ljl[N];
bool cmp(const int&a,const int&b){return ljl[a].tt<ljl[b].tt;}

il void Solve(rgt Zt)
{
    top=Res=0;
    for(rgt i=1;i<=n;++i)
        if(Zt&(1<<(i-1)))sgn[++top]=i,Res+=ljl[i].gt;
    if(Res<Ans)return;
    sort(&sgn[1],&sgn[top+1],cmp);
    rg lst pdc=1,rest=0;
    rg bool flag=true;
    for(rgt i=0;i<top;++i)
    {
        rg lst nd=0,brk=Inf;
        for(rgt j=i+1;j<=top;++j)
        {
            nd+=ljl[sgn[j]].gv;
            rg lst tm=ljl[sgn[j]].tt-ljl[sgn[i]].tt;
            rg lst b=pdc-tm,c=nd-rest-pdc*tm;
            if(b*b-4*c<0){flag=false;break;}//delta
            rg lst x=(sqrt(b*b-4*c)-b)/2;
            brk=MIN(brk,x);
        }pdc+=brk;
        rest+=pdc*(ljl[sgn[i+1]].tt-ljl[sgn[i]].tt-brk)-ljl[sgn[i+1]].gv;
        if(!flag||brk<0||rest<0){flag=false;break;}
    }if(flag)Ans=MAX(Ans,Res);
}

int main()
{
    n=read(),UP=(1<<n);
    for(rgt i=1;i<=n;++i)
        ljl[i]=(DD){read(),read(),read()};
    for(rgt i=1;i<UP;++i)Solve(i);
    return printf("%lld\n",Ans),0;
}