题意概述
有m组物品,每组物品有a[i]个,你可以向其中插入k块板从而使它们断开,使其价值和最小。
价值和定义:假设连续一段未断开的同组物品长度为L,则其价值为L*(L+1)/2
题解
对于一组物品来说,尽量使其均分(每段长之差不超过1)是最优的划分方法。
简单证明如下:
将一组分为n和n+2个物品,其价值和为
n
∗
(
n
+
1
)
2
+
(
n
+
2
)
∗
(
n
+
3
)
2
=
n
2
+
3
n
+
3
\frac{n*(n+1)}{2}+\frac{(n+2)*(n+3)}{2}=n^2+3n+3
2n∗(n+1)+2(n+2)∗(n+3)=n2+3n+3
而将其均分,分为n+1和n+1个物品,其价值和为
(
n
+
1
)
∗
(
n
+
2
)
2
∗
2
=
n
2
+
3
n
+
2
\frac{(n+1)*(n+2)}{2}*2=n^2+3n+2
2(n+1)∗(n+2)∗2=n2+3n+2
显然均分时价值和更小
举个例子,将6个物品分成4组(即插入3块板)。
○○|○○|○|○是最优的分法
考虑使用动态规划解决
f[i][j]表示第i组物品,已经插了j块板时最小的价值和
因此枚举前i-1组中共插入几块板即可,可得状态转移方程:
f
[
i
]
[
j
]
=
m
a
x
(
f
[
i
−
1
]
[
k
]
+
c
a
l
c
(
a
[
i
]
,
j
−
k
)
)
f[i][j]=max(f[i-1][k]+calc(a[i],j-k))
f[i][j]=max(f[i−1][k]+calc(a[i],j−k))
calc(n,k)即表示在n个物品中插入k块板时的价值
接下来的难点就是如何计算该价值
需注意插入k块板时将n个物品分为了k+1组
显然均分后的组的最小值为
w
i
d
=
n
k
+
1
wid=\frac{n}{k+1}
wid=k+1n 则最大值为
w
i
d
+
1
wid+1
wid+1
大小为
w
i
d
wid
wid的组共有
s
1
=
(
w
i
d
+
1
)
∗
(
k
+
1
)
−
n
s1=(wid+1)*(k+1)-n
s1=(wid+1)∗(k+1)−n组 (鸡兔同笼告诉你怎么算)
则大小为
w
i
d
+
1
wid+1
wid+1的组共有
s
2
=
(
k
+
1
)
−
s
1
s2=(k+1)-s1
s2=(k+1)−s1
于是代码可以容易地写出。
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define Maxn 510
#define int long long
#define INF 0x3f3f3f3f
using namespace std;
int n,m,s,a[Maxn],f[Maxn][Maxn];
int read()
{
int s=0,w=1;char ch=getchar();
while (!isdigit(ch))
{
if (ch=='-') w=-1;
ch=getchar();
}
while (isdigit(ch)) s=(s<<1)+(s<<3)+ch-'0',ch=getchar();
return s*w;
}
signed main()
{
n=read(),m=read(),s=read();
for (int i=1;i<=n;i++)
{
int x=read();
a[x]++;
}
for (int i=1;i<=m;i++)
for (int j=0;j<=s;j++)
{
f[i][j]=INF;
for (int k=0;k<=j;k++)
{
int now=j-k,wid=a[i]/(now+1),s1=(wid+1)*(now+1)-a[i],s2=(now+1)-s1;
f[i][j]=min(f[i][j],f[i-1][k]+s1*wid*(wid+1)/2+s2*(wid+1)*(wid+2)/2);
}
}
printf("%lld\n",f[m][s]);
return 0;
}