恩,贪心,鉴定完毕。
一个物品是否放进来,取决于它是否能对答案做出贡献。
那物品i的贡献就是(w[i]-r[i])
可是收益的减少是会叠加的
那就是(w[i]-j*r[i]),j表示选择物品i后又选择的物品数量
可是我怎么知道选择i后又会选择几件物品啊
那么我们引入一个新的值(d[i]=w[i]/r[i]),表示若使物品i对答案有贡献,选择物品i后最多再选择d件物品
既然这样,我们也有点眉目了,dfs啊
很好,写的很漂亮,50。。。TLE
dfs
看来是不能再优化了
那让我们退回去,往前看“可是我怎么知道选择i后又会选择几种物品啊”
好像有一种方法可以知道还会再选几件,没错,你是不是也想到了,就是 dfs 动规
我们用(f[i][j])表示在前i种物品中选择j件,可是这怎么记忆之前所说的j呢?
还记得之前说好的贪心吗,这里继续贪。
我们把物品按照r从大到小的顺序排序,(f[i][j])表示i件物品选择j件且最先选择j件时的收益
这里的贪心很好证明,既然r要取多次,那么我们自然默认让更小的r选择更多的次数
下面是代码
dfs版
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cctype>
#define ll long long
#define gc() getchar()
#define maxn 3005
using namespace std;
inline ll read(){
ll a=0;int f=0;char p=gc();
while(!isdigit(p)){f|=p=='-';p=gc();}
while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=gc();}
return f?-a:a;
}
void write(ll a){
if(a>9)write(a/10);
putchar(a%10+'0');
}
int n;
struct ahaha{
int w,r,d;
friend bool operator < (ahaha x,ahaha y){
return x.d>y.d;
}
}a[maxn];
bool c[maxn]; //表示物品是否被选择过
int ans;
inline int max(int x,int y){return x>y?x:y;}
inline int min(int x,int y){return x<y?x:y;}
void dfs(int sum,int sr,int sy){ //sum表示当前收益,sr表示需要累计下去的r,sy表示最多还能选择sy个数贪心就不优了
ans=max(ans,sum); //因为不知道选多少个数,所以ans每步比较
if(!sy)return; //如果不能再选 返回
for(int i=1;i<=n;++i){
if(c[i])continue;
c[i]=1;
dfs(sum+a[i].w-sr,sr+a[i].r,min(sy-1,a[i].d)); //sy应取最小值
c[i]=0;
}
}
inline void solve(){
for(int i=1;i<=n;++i){
c[i]=1;
dfs(a[i].w,a[i].r,a[i].d);
c[i]=0;
}
}
int main(){
n=read();
for(int i=1;i<=n;++i)a[i].w=read(),a[i].r=read(),a[i].d=a[i].w/a[i].r;
sort(a+1,a+n+1);
solve();
write(ans);
return 0;
}
DP版
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cctype>
#define ll long long
#define gc() getchar()
#define maxn 3005
using namespace std;
inline ll read(){
ll a=0;int f=0;char p=gc();
while(!isdigit(p)){f|=p=='-';p=gc();}
while(isdigit(p)){a=(a<<3)+(a<<1)+(p^48);p=gc();}
return f?-a:a;
}
void write(ll a){
if(a>9)write(a/10);
putchar(a%10+'0');
}
int n,f[maxn][maxn],ans;
struct ahaha{
int w,r;
friend bool operator < (ahaha x,ahaha y){
return x.r>y.r;
}
}a[maxn];
int main(){
n=read();
for(int i=1;i<=n;++i)a[i].w=read(),a[i].r=read();
sort(a+1,a+n+1);
f[1][1]=a[1].w;
for(int i=2;i<=n;++i)
for(int j=1;j<=i;++j)
f[i][j]=max(f[i-1][j],f[i-1][j-1]+a[i].w-a[i].r*(j-1)); //表示选择i个物品时,选择物品i和不选物品i两种操作
for(int i=1;i<=n;++i)ans=max(ans,f[n][i]);
write(ans);
return 0;
}