最优得分

题目大意

n道题目,在第x分钟做对第i道题得Ai-Bi*x分,第i题一旦开始做就得连续的做Ci分钟。
给你m分钟,每道题目当然只能做一次,求最大得分。

偏序关系

如果我们已经决定了要做哪些题,如何分配最优顺序?
考虑对已知解得优化,假如做完第j道题后立刻做第k道题,交换这两题的做题顺序有没有可能更优?
不交换没有交换了优?那么

AjBjCj+AkBk(Cj+Ck)<AkBkCk+AjBj(Cj+Ck)

简化得
Bj/Cj<Bk/Ck

这是严格的偏序关系,而且具备传递性。

DP

按照偏序关系排序后,我们设f[i]表示选到第i题,然后可以枚举上一道做的题。

#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define fo(i,a,b) for(i=a;i<=b;i++)
using namespace std;
typedef long long ll;
typedef double db;
const int maxn=3000+10;
int a[maxn],b[maxn],c[maxn],id[maxn],p[maxn],f[maxn][maxn];
bool bz[maxn];
int i,j,k,l,t,n,m,ans,ca;
int read(){
    int x=0,f=1;
    char ch=getchar();
    while (ch<'0'||ch>'9'){
          if (ch=='-') f=-1;
          ch=getchar();
    }
    while (ch>='0'&&ch<='9'){
          x=x*10+ch-'0';
          ch=getchar();
    }
    return x*f;
}
bool cmp(int j,int k){
     return (ll)b[j]*c[k]<(ll)b[k]*c[j];
}
void calc(){
     int i,j,k=0,t=0;
     fo(i,1,n)
         if (bz[i]) id[++k]=i;
     fo(i,1,k-1)
         fo(j,1,k-1)
             if (cmp(id[j],id[j+1])) swap(id[j],id[j+1]);
     i=0;
     fo(j,1,k){
         i+=c[id[j]];
         t+=a[id[j]]-b[id[j]]*i;
     }
     ans=max(ans,t);
}
void dfs(int x,int y){
     if (y>m) return;
     if (x==n+1){
         calc();
         return;
     }
     bz[x]=1;
     dfs(x+1,y+c[x]);
     bz[x]=0;
     dfs(x+1,y);
}
void work(){
     ans=0;
     fo(i,1,n) bz[i]=0;
     dfs(1,0);
     printf("%d\n",ans);
}
bool cmp2(int x,int y){
    return (db)b[x]/c[x]>(db)b[y]/c[y];
}
int main(){
    freopen("score.in","r",stdin);freopen("score.out","w",stdout);
    ca=read();
    while (ca--){
          n=read();m=read();
          fo(i,1,n) a[i]=read(),b[i]=read(),c[i]=read();
          if (n<=10){
              work();
              continue;
          }
          fo(i,1,n) id[i]=i;
          sort(id+1,id+n+1,cmp2);
          fo(i,1,n) p[i]=c[id[i]];
          fo(i,1,n) c[i]=p[i];
          fo(i,1,n) p[i]=b[id[i]];
          fo(i,1,n) b[i]=p[i];
          fo(i,1,n) p[i]=a[id[i]];
          fo(i,1,n) a[i]=p[i];
          fo(i,0,n)
             fo(j,0,m)
                f[i][j]=0;
          ans=0;
          fo(i,1,n)
              fo(j,1,m){
                 f[i][j]=f[i-1][j];
                 if (j<c[i]) continue;
                 f[i][j]=max(f[i][j],f[i-1][j-c[i]]+a[i]-b[i]*j);
                 ans=max(ans,f[i][j]);
              }
          printf("%d\n",ans);
    }
    return 0;
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值