CH 5105 Cookies(贪心+DP)

\(CH 5105 Cookies\)



\(solution:\)

真是好题一道!这道题我想了很久很久,就得这一题可以直接完全贪心,可惜最后还是失败了,但是对贪心的深入思考也换来了一个最优解方案。然后这一题的DP也考的很有技术!

贪心1:这一道题当时第一眼就是贪心。首先不管每一个孩子的贪婪度,只要我们将糖果分为 \(N\) 份,实际上就已经确定了会存在多少个孩子他们的糖果数比多少个其他孩子要少。于是我们贪心的让贪婪度小的孩子去做那些糖果数比多少个其他孩子要少的孩子。(虽然这在伦理上说不过去)

贪心2,它可以减少数据范围:如果某些孩子的糖果数一样那么他们彼此之间就不会产生怨气。所以当我们将糖果平均分时,产生的怨气最少(就是没有怨气)。但是我们的糖果数不一定就能平均分了,但是我们依旧可以贪心的让相同糖果数的人尽量多。其次我们可以想到:如果糖果不够平均分,那么一定存在几个人他们的糖果数比其他所有人都低!这样我们就能产生一个很自然的想法:先平均分,如果还剩下几个不够让所有的孩子在多拿一个糖果,就钦定从贪婪度嘴小的孩子里拿回糖果给其他的孩子使得除了这个孩子以外其他孩子都是平均分的(真的好不公平啊),这样肯定能最优。但是我们同样不能保证把这个孩子的所有糖果都拿回能使得(除了这个孩子以外其他孩子都是平均分的)。但是这种情况\(m>n^2\)的时候可以得到保证,多以我们可以将 \(m\) 限制在 \(n^2\) 以内,于是数据范围就被我们消减了,然后博主就拿到了CH上的最优解。。。。。。

然后讲一下DP,这道题的DP比较难想,因为我们首先需要想到第一个贪心,然后我们发现贪婪度小的孩子分的糖果少,于是我们按照贪婪度从大到小排序,这就符合我们线性DP的要求了。我完全可以设 \(F[i][j]\) 表示前 \(i\) 个孩子用了 \(j\) 颗糖果。但是这一题DP也难快速转移,我们如果正常转移花费的时间很多,会超时(如果我们用第二个贪心减少数据范围,也可以跑过去)。于是我们需要优化,这时候我们发现由于后面的孩子因为贪婪度小分到的糖果少一些,于是我们可以转移一下:假如第 \(i+1\) 个孩子比第 \(i\) 个孩子得到的糖果少一个,那么这就相当于前 \(i\) 个孩子都多拿了一个糖果(这两个想法是等效的)。于是转移方程变成了:

\(F[i][j]=\{^{F[i][j-i]}_{^{ min}_{0\leq k<i}\{F[k][j-(i-k)]+k*(S[i]-S[k]) \}}\)

注: \(S[i]\) 表示前 \(i\) 个孩子的贪婪度总和。



\(code:\)

#include<iostream>
#include<cstdio>
#include<iomanip>
#include<algorithm>
#include<cstring>
#include<cstdlib>
#include<ctime>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>

#define ll long long
#define db double
#define inf 0x7fffffff
#define rg register int

using namespace std;

int n,m;
int S[35];
int as[35];
int f[35][905];

struct su{
    int da,id;
    inline bool operator <(const su &x)const{return da>x.da;}
}a[35];

struct pi{
    int x,y;
}d[35][905];

inline int qr(){
    register char ch; register bool sign=0; rg res=0;
    while(!isdigit(ch=getchar())) if(ch=='-')sign=1;
    while(isdigit(ch)) res=res*10+(ch^48),ch=getchar();
    return sign?-res:res;
}

inline void tepan(){
    printf("%d\n",a[n].da*(n-1));
    rg ans=m/n,res=m%n;
    for(rg i=1;i<n;++i) as[a[i].id]=ans+bool(res);
    as[a[n].id]=ans; if(res) as[a[n].id]-=n-res-1;
    for(rg i=1;i<=n;++i) printf("%d ",as[i]);
    puts(""); exit(0);
}

inline void print(int i,int j){
    if(i==0)return ;
    else print(d[i][j].x,d[i][j].y);
    if(d[i][j].x==i) for(rg k=1;k<=i;++k)++as[a[k].id];
    else for(rg k=d[i][j].x+1;k<=i;++k)++as[a[k].id];
}

int main(){
    //freopen(".in","r",stdin);
    //freopen(".out","w",stdout);
    n=qr(); m=qr();
    for(rg i=1;i<=n;++i)
        a[i].da=qr(),a[i].id=i;
    sort(a+1,a+n+1); if(m>n*n)tepan();
    for(rg i=1;i<=n;++i) S[i]=S[i-1]+a[i].da;
    for(rg i=1;i<=m;++i) f[0][i]=1e9;
    for(rg i=1;i<=n;++i){
        for(rg j=0;j<=m;++j){
            if(j<i){f[i][j]=1e9; continue;}
            f[i][j]=f[i][j-i]; d[i][j].x=i; d[i][j].y=j-i;
            for(rg k=0;k<i;++k){
                rg tot=f[k][j-(i-k)]+k*(S[i]-S[k]);
                if(tot<f[i][j])f[i][j]=tot,d[i][j].x=k,d[i][j].y=j-(i-k);
            }
        }
    } printf("%d\n",f[n][m]); print(n,m);
    for(rg i=1;i<=n;++i) printf("%d ",as[i]);
    puts(""); return 0;
}

猜你喜欢

转载自www.cnblogs.com/812-xiao-wen/p/11010537.html