蓝书(算法竞赛进阶指南)刷题记录——CH5105 Cookies(贪心+DP)

题目:CH5105.
题目大意:现在有 n n n个人 m m m块饼干,其中第 i i i个人有一个属性 g [ i ] g[i] g[i].现在要求分配饼干,每个人都要分到至少一块饼干,若有 a [ i ] a[i] a[i]个人分到的饼干多于第 i i i个人,则他的怒气值为 a [ i ] ∗ g [ i ] a[i]*g[i] a[i]g[i],求最小怒气值以及一种方案.
1 ≤ n ≤ 30 , 1 ≤ n ≤ m ≤ 5 ∗ 1 0 3 1\leq n\leq 30,1\leq n\leq m\leq 5*10^3 1n30,1nm5103.

贪心地想, g [ i ] g[i] g[i]越大的人分到的饼干肯定越多.

那么先按照 g [ i ] g[i] g[i]从大到小排序,然后考虑DP.设 f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i个人 j j j块饼干的最小怒气值,可以列出方程…等等这方程怎么列,这个状态里面并没有记录第 i i i个人有几块饼干啊…

赶紧看蓝书.jpg

考虑若第 i i i个人要得到大于 1 1 1块饼干,那么每多 1 1 1块饼干就让他前面的人多 1 1 1块饼干就好了.

而如果第 i i i个人只要一块饼干,那么就大力枚举前面有几个人也只要一块饼干即可.

也就是说:
f [ i , j ] = min ⁡ { f [ i ] [ j − i ] , min ⁡ k = 0 i − 1 { f [ k ] [ j − ( i − k ) ] + k ∑ t = k + 1 i g [ t ] } } f[i,j]=\min \left\{ f[i][j-i],\min_{k=0}^{i-1}\left\{ f[k][j-(i-k)]+k\sum_{t=k+1}^{i}g[t] \right\} \right\} f[i,j]=min{f[i][ji],k=0mini1{f[k][j(ik)]+kt=k+1ig[t]}}

可以通过对 g g g求前缀和优化求和式的复杂度,时间复杂度 O ( n 2 m ) O(n^2m) O(n2m).

还要考虑求方案,直接记录一下上一个状态即可.

代码如下:

#include<bits/stdc++.h>
  using namespace std;
 
#define Abigail inline void
typedef long long LL;

const int N=30,M=5000,INF=(1<<30)-1;

int n,m;
struct person{
  int g,id;
}a[N+9];
int ans[N+9];

bool cmp(const person &a,const person &b){return a.g>b.g;}

struct state{
  int v,h0,h1;
}dp[N+9][M+9];

void Dfs_plan(int n0,int n1){
  if (!n0) return;
  Dfs_plan(dp[n0][n1].h0,dp[n0][n1].h1);
  if (n0==dp[n0][n1].h0)
    for (int i=1;i<=n0;++i) ++ans[a[i].id];
  else
    for (int i=dp[n0][n1].h0+1;i<=n0;++i) ans[a[i].id]=1;
}

Abigail into(){
  scanf("%d%d",&n,&m);
  for (int i=1;i<=n;++i){
    scanf("%d",&a[i].g);
    a[i].id=i;
  }
}

Abigail work(){
  sort(a+1,a+1+n,cmp);
  for (int i=1;i<=n;++i) a[i].g+=a[i-1].g;
  for (int i=0;i<=n;++i)
    for (int j=0;j<=m;++j)
      dp[i][j].v=INF;
  dp[0][0].v=0;
  for (int i=1;i<=n;++i)
    for (int j=i;j<=m;++j){
      dp[i][j].v=dp[i][j-i].v;
      dp[i][j].h0=i;dp[i][j].h1=j-i;
      for (int k=0;k<i;++k)
        if (dp[k][j-i+k].v+k*(a[i].g-a[k].g)<dp[i][j].v){
          dp[i][j].v=dp[k][j-i+k].v+k*(a[i].g-a[k].g);
          dp[i][j].h0=k;dp[i][j].h1=j-i+k;
		}
	}
  Dfs_plan(n,m);
}
 
Abigail outo(){
  printf("%d\n",dp[n][m].v);
  for (int i=1;i<=n;++i)
    printf("%d ",ans[i]);
  puts("");
}

int main(){
  into();
  work();
  outo();
  return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值