1. 程式人生 > >洛谷 P2647 最大收益

洛谷 P2647 最大收益

ostream read == git 疊加 ref () tle char

我是題面

恩,貪心,鑒定完畢。

一個物品是否放進來,取決於它是否能對答案做出貢獻。

那物品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;
}

洛谷 P2647 最大收益