题目: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
1≤n≤30,1≤n≤m≤5∗103.
贪心地想, 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][j−i],k=0mini−1{f[k][j−(i−k)]+kt=k+1∑ig[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;
}