每个孩子有一个贪婪度,第 i 个孩子的贪婪度为 g[i]。
如果有 a[i] 个孩子拿到的饼干数比第 i 个孩子多,那么第 i 个孩子会产生 g[i]×a[i] 的怨气。
给定 N、M 和序列 g,圣诞老人请你帮他安排一种分配方式,使得每个孩子至少分到一块饼干,并且所有孩子的怨气总和最小。
注意到怨气值只和相对大小有关,而和绝对大小没有关系 可以考虑放缩来做
排序后
\(f[i][j]=f[i][j-i]\) 每个人去掉一块饼干不影响结果
\(f[i][j]=min\{f[i-k][j-k]+k*\sum_{p=k+1}^ig[p] \}\) 枚举最后有多少个饼干
由于最后是取最小值 所以即使状态之间有重叠,正确性仍然可以保证
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define pa pair<int,int>
#define mp make_pair
#define fs first
#define sc second
using namespace std;
const int N=35;
const int M=5050;
int read()
{
int x=0,f=0,c=getchar();
while(c<'0'||c>'9'){if(c=='-')f=1;c=getchar();}
while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
return f?-x:x;
}
struct Node
{
int g,id;
}p[N];
bool cmp(Node a,Node b){return a.g>b.g;}
int sum[N],n,m;
int f[N][M],ans[N];
pa g[N][M];
void print(int x,int y,pa pp)
{
if(!x) return;
if( x==pp.fs)
for(int i=1;i<=x;i++) ans[p[i].id]++;
else
for(int i=pp.fs+1;i<=x;i++) ans[p[i].id]++;
print(pp.fs,pp.sc,g[pp.fs][pp.sc]);
}
int main()
{
n=read(); m=read();
for(int i=1;i<=n;i++) p[i].g=read(),p[i].id=i;
sort(p+1,p+n+1,cmp);
for(int i=1;i<=n;i++) sum[i]=sum[i-1]+p[i].g;
//dp初始状态的分析 根据含义边界的值是多少 所以应该怎么去赋值 初始状态也要合法
memset(f,0x3f,sizeof f);
for(int i=1;i<=n;i++)
for(int j=i;j<=m;j++)
{
if(i==j){f[i][j]=0; continue;}
for(int k=0;k<i;k++)
if(f[k][j-i+k]+k*(sum[i]-sum[k])<f[i][j])
f[i][j]=f[k][j-i+k]+k*(sum[i]-sum[k]),
g[i][j]=mp(k,j-i+k);
if(f[i][j-i]<f[i][j])
f[i][j]=f[i][j-i],
g[i][j]=mp(i,j-i);
}
printf("%d\n",f[n][m]);
print(n,m,g[n][m]);
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
return 0;
}