版权声明:转载请注明原出处啦QAQ(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/90611656
题目:CH5105.
题目大意:现在有
个人
块饼干,其中第
个人有一个属性
.现在要求分配饼干,每个人都要分到至少一块饼干,若有
个人分到的饼干多于第
个人,则他的怒气值为
,求最小怒气值以及一种方案.
.
贪心地想, 越大的人分到的饼干肯定越多.
那么先按照 从大到小排序,然后考虑DP.设 表示前 个人 块饼干的最小怒气值,可以列出方程…等等这方程怎么列,这个状态里面并没有记录第 个人有几块饼干啊…
赶紧看蓝书.jpg
考虑若第 个人要得到大于 块饼干,那么每多 块饼干就让他前面的人多 块饼干就好了.
而如果第 个人只要一块饼干,那么就大力枚举前面有几个人也只要一块饼干即可.
也就是说:
可以通过对 求前缀和优化求和式的复杂度,时间复杂度 .
还要考虑求方案,直接记录一下上一个状态即可.
代码如下:
#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;
}