题目:
题目链接:https://jzoj.net/senior/#main/show/3920
FJ有M个牛棚,编号1至M,刚开始所有牛棚都是空的。FJ有N头牛,编号1至N,这N头牛按照编号从小到大依次排队走进牛棚,每一天只有一头奶牛走进牛棚。第i头奶牛选择走进第p[i]个牛棚。由于奶牛是群体动物,所以每当一头奶牛x进入牛棚y之后,牛棚y里的所有奶牛们都会喊一声“欢迎欢迎,热烈欢迎”,由于声音很大,所以产生噪音,产生噪音的大小等于该牛棚里所有奶牛(包括刚进去的奶牛x在内)的数量。FJ很讨厌噪音,所以FJ决定最多可以使用K次“清空”操作,每次“清空”操作就是选择一个牛棚,把该牛棚里所有奶牛都清理出去,那些奶牛永远消失。“清空”操作只能在噪音产生后执行。现在的问题是:FJ应该选择如何执行“清空”操作,才能使得所有奶牛进入牛棚后所产生的噪音总和最小?
思路:
我们发现,不同牛棚之间是互不相干的,而牛的先后顺序也是无关的。所以可以大胆猜想时间复杂度与n无关
我们记录进入每一个牛棚的牛的数量,设
f
[
i
]
[
j
]
f[i][j]
f[i][j]表示前
i
i
i个牛棚,用了
j
j
j次清空的最小噪音。
那么再枚举这一个牛棚清除噪音次数
k
k
k,那么显然把这
k
+
1
k+1
k+1段平均分配会最优。
那么维护一下等差数列就好了。
时间复杂度
O
(
m
k
2
)
O(mk^2)
O(mk2)
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=110,M=510;
int n,m,t;
ll ans,f[N][M],cnt[N];
int main()
{
scanf("%d%d%d",&n,&m,&t);
for (int i=1,x;i<=n;i++)
{
scanf("%d",&x);
cnt[x]++;
}
f[0][0]=0;
for (int i=1;i<=m;i++)
for (int j=0;j<=t;j++)
{
f[i][j]=f[i-1][j]+(1+cnt[i])*cnt[i]/2;
for (int k=1;k<=min((ll)j,cnt[i]-1);k++)
{
ll r=(cnt[i]-1)/(k+1),rr=(cnt[i]-1)%(k+1)+1;
ll s=(1+r)*r/2*(k+1)+rr*(r+1);
f[i][j]=min(f[i][j],f[i-1][j-k]+s);
}
}
ans=1e17;
for (int i=0;i<=t;i++)
ans=min(ans,f[m][i]);
printf("%lld",ans);
return 0;
}