噪音
题目描述:
FJ有M个牛棚,编号1至M,刚开始所有牛棚都是空的。FJ有N头牛,编号1至N,这N头牛按照编号从小到大依次排队走进牛棚,每一天只有一头奶牛走进牛棚。第i头奶牛选择走进第p[i]个牛棚。由于奶牛是群体动物,所以每当一头奶牛x进入牛棚y之后,牛棚y里的所有奶牛们都会喊一声“欢迎欢迎,热烈欢迎”,由于声音很大,所以产生噪音,产生噪音的大小等于该牛棚里所有奶牛(包括刚进去的奶牛x在内)的数量。FJ很讨厌噪音,所以FJ决定最多可以使用K次“清空”操作,每次“清空”操作就是选择一个牛棚,把该牛棚里所有奶牛都清理出去,那些奶牛永远消失。“清空”操作只能在噪音产生后执行。现在的问题是:FJ应该选择如何执行“清空”操作,才能使得所有奶牛进入牛棚后所产生的噪音总和最小?
输入:
第一行,N、M、K。
接下来有N行,每行一个整数,第i行是p[i]。
输出:
最小的噪音总和。
这道题很明显是
D
P
DP
DP啊。但是我很不理解坐在我斜前方的红衣大佬说是斜率优化。
这道题注意到不同牛栏可以分开考虑。
设
f
i
,
j
f_{i,j}
fi,j表示第
i
i
i个牛栏,
j
j
j次清空的机会的最小噪音。
然后很显然要平均分配。
然后跑
D
P
DP
DP。
/*
事已至此,只能膜拜2017张晋杰大佬。
stO ZJJ Orz,NOI Au,IOI Au
*/
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const ll N = 1000010;
const ll M = 110,K = 510;
inline ll min(ll a,ll b) { return a < b ? a : b; }
inline void read(ll &x)
{
char ch = getchar(); x = 0;
for(;ch < '0' || ch > '9';) ch = getchar();
for(;ch >= '0' && ch <= '9';) x = x * 10 + (ch ^ '0'), ch = getchar();
}
ll n,m,k,f[M][K],a[N],buc[M],ans[M][K],mx = 0x3f3f3f3f;
int main()
{
read(n), read(m), read(k);
for(ll i = 1;i <= n; ++ i) read(a[i]), ++buc[a[i]];
memset(ans,0x3f,sizeof ans);
for(ll i = 1;i <= m; ++ i)
{
f[i][0] = (buc[i] + 1) * buc[i] / 2;
for(ll j = 1;j <= k; ++ j)
{
ll tmp = buc[i] / (j + 1),t = buc[i] - tmp * (j + 1),cnt = 0;
cnt = (tmp + 1) * t + (tmp + 1) * tmp / 2 * (j + 1);
f[i][j] = cnt;
}
}
for(ll i = 0;i <= k; ++ i) ans[1][i] = f[1][i];
for(ll i = 2;i <= m; ++ i)
for(ll j = 0;j <= k; ++ j)
{
ll mn = mx;
for(ll l = 0;l <= j; ++ l)
mn = min(mn,ans[i - 1][j - l] + f[i][l]);
ans[i][j] = mn;
}
for(ll i = 0;i <= k; ++ i) mx = min(mx,ans[m][i]);
printf("%lld",mx);
return 0;
}
结果平均分配打错了。。。自闭了一个小时,然后调出来了。
最后发现要开
l
o
n
g
l
o
n
g
long long
longlong草草全开然后收场了。