JZOJ3920 噪音

噪音

题目描述:
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 啊。但是我很不理解坐在我斜前方的红衣大佬说是斜率优化。
这道题注意到不同牛栏可以分开考虑。
f i , j f_{i,j} 表示第 i i 个牛栏, j j 次清空的机会的最小噪音。
然后很显然要平均分配。
然后跑 D P 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 草草全开然后收场了。

猜你喜欢

转载自blog.csdn.net/INnovate2030/article/details/103435111