【题解】CF1056F Write the Contest(三分+贪心+DP)

【题解】CF1056F Write the Contest(三分+贪心+DP)

最优化问题的三个解决方法都套在一个题里了,真牛逼

最优解应该是怎样的,一定存在一种最优解是先完成了耗时长的任务再干别的(不干白不干啊),所以我们按照耗时先排序。

假设你最优解是去事件\(e_1,e_2,e_3,e_4\),你可以在规定时间里干完,那么你如果按照耗时从大往小干也一定可以干完。

好像只能找到"按照耗时从大往小干"一种钦定方法使得所有方案可以归纳到这种情况

考虑最终耗时是怎样的:\(t\)表示练习用的时间
\[ f(t)=\dfrac {\sum(\dfrac {10} 9)^i a_{h_i}} {s_0+Ct}+10t \]
我们转化一下思路,设\(dp(i,j)\)表示选择\(i\)个任务做并且将会获得收益\(j\)的的最小的\(\sum(\dfrac {10} 9)^i a_{h_i}\),这样的设置状态类似于那个Jury一题,【题解】Jury Compromise(链表+DP)。转移显然不讲了。

现在我们要使得\(f(t)=\dfrac {dp(i,j)} {s_0+Ct}+10t\)满足条件并且使得\(j\)最大,由于\(i,j\le O(n)\)所以直接\(n^2\)枚举即可,现在的问题就变成了最小化这个东西\(f(t)\),显然这个函数有单峰且最小(类双勾函数),所以直接三分即可。

//@winlere
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>


using namespace std;  typedef long long ll;
inline int qr(){
      register int ret=0,f=0;
      register char c=getchar();
      while(c<48||c>57)f|=c==45,c=getchar();
      while(c>=48&&c<=57) ret=ret*10+c-48,c=getchar();
      return f?-ret:ret;
}
const int maxn=1001;
long double dp[maxn][maxn];
const long double delta=(long double)9/10;
const long double eps=1e-5;
typedef pair < int , int > P;
P data[maxn];
//    a    c

#define f(x) ((  (x)+dp[t][k]/(1+C*(x))   ))
      
long double mi[maxn];
int main(){
      //freopen("gamemag.in","r",stdin);
      //freopen("gamemag.out","w",stdout);
      mi[0]=1;
      for(register int t=1;t<maxn;++t) mi[t]=mi[t-1]/delta;
      for(register int T00=qr();T00;--T00){
        int n;
        long double C,T;
        cin>>n>>C>>T;
        for(register int t=1;t<=n;++t)
          data[t].first=qr(),data[t].second=qr();
        sort(data+1,data+(int)n+1,[](const P&a,const P&b){return a>b;});
        for(register int t=0;t<maxn;++t)
          for(register int i=0;i<maxn;++i)
            dp[t][i]=1e18;
        dp[0][0]=0;
        for(register int t=1;t<=n;++t){
          for(register int i=t;i;--i){
            for(register int k=data[t].second;k<=i*10;++k){
                  dp[i][k]=min(dp[i][k],dp[i-1][k-data[t].second]+data[t].first*mi[i]);
            }
          }
        }
        int ans=0;
        for(register int t=1;t<=n;++t){
          for(register int k=ans+1;k<=10*t;++k){
            //cout<<dp[t][k]<<endl;
            long double l=0,r=T,ll,rr;
            do{
                  ll=l+(r-l)/3;
                  rr=r-(r-l)/3;
                  if(f(ll)<f(rr)) r=rr;
                  else l=ll;
            }while(l+eps<r);
            if(f(l)+10ll*t<T) ans=max(ans,k);
          }
        }
        cout<<ans<<endl;
      }
      return 0;
}

转载于:https://www.cnblogs.com/winlere/p/11283919.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值