五一模拟赛 噪声 线性DP

版权声明:https://blog.csdn.net/huashuimu2003 https://blog.csdn.net/huashuimu2003/article/details/89763575

title

【问题描述】

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]。

【输出】

一个整数,最小的噪音总和。

【输入输出样例1】

noise.in
5 1 2
1
1
1
1
1
noise.out
7

样例解释1:

第1头奶牛进入牛棚且产生噪音后,“清空”牛棚。第3头奶牛进入牛棚且产生噪音后,再次“清空”牛棚。5头奶牛产生的噪音依次是:1,1,2,1,2。如果没有“清空”操作,5头奶牛产生的噪音依次是:1,2,3,4,5。

【输入输出样例2】

noise.in
11 2 3
1
2
1
2
1
2
1
2
1
2
1
noise.out
18

样例解释2:

第3头奶牛进入牛棚1且产生噪音后,“清空”牛棚1。第7头奶牛进入牛棚1且产生噪音后,“清空”牛棚1。第6头奶牛进入牛棚2且产生噪音后,“清空”牛棚2。

【数据范围】

对于40%的数据,M=1。
对于60%的数据,1<=N <= 1000。
对于80%的数据,1<=N <= 50000。
对于100%数据, 1<=N <= 1000000, 1<=M<=100, 1<=K<= 500。

  • 40 p t s 40pts
    样例中给定的 m = 1 m=1 的情况,
    分析一下可以发现我们要尽可能的把一个牛棚均匀分成 k k 段,
    那么我们只需要在 k + 1 k+1 等分点上清空就好了。
    通过暴力可以得到 40 40 分.

  • 80 p t s 80pts
    考虑贪心,我们将清空次数尽量分给牛多的牛棚,可以骗到 80 80 分.

  • 100 p t s 100pts :是 d p dp (惊不惊喜,意不意外)。
    m = 1 m=1 的情况可以告诉我们第 i i 个牛棚清空 j j 次都是有最优解的,那么我们可以求出 f [ i ] [ j ] f[i][j] ,表示第 i i 个牛棚清空 j j 次的最小噪音值。 这样我们可以枚举第几个牛棚清空几次。然后可以发现这就是背包问题,再开一个数组 g [ i ] [ j ] g[i][j] 表示 1 i 1\sim i 个牛棚中清空 j j 次的最小噪音值,就可以解决了。

    • 求出 f [ i ] [ j ] f[i][j] 是一个难点。假设我们要把第 i i 个牛棚清空 j j 次,那么实际上我们就是把第 i i 个牛棚分成了 j + 1 j+1 段,我们的目的是把每一段的牛的数量尽可能的靠近 n u m [ i ] / ( j + 1 ) num[i] / (j + 1) ,但是有可能不能平均分,这个时候就有一些段要分 n u m [ i ] / ( j + 1 ) + 1 num[i] / (j + 1) + 1 头牛,具体有多少段呢? n u m [ i ] % ( j + 1 ) num[i] \% (j + 1) ,平均分的段数自然也就出来了。
    • 这道题的关键就是各个牛棚之间不互相影响,那么对于每一个局部部分我们可以先算出局部最优解,最后合并成全局最优解,是一种通用的技巧。

code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e6+10,maxm=110,maxk=510;

template<typename T>inline void read(T &x)
{
	x=0;
	T f=1, ch=getchar();
	while (!isdigit(ch) && ch^'-') ch=getchar();
	if (ch=='-') f=-1, ch=getchar();
	while (isdigit(ch)) x=(x<<1)+(x<<3)+(ch^48), ch=getchar();
	x*=f;
}

inline ll C(ll x)
{
	return x*(x+1)>>1;
}

ll num[maxn],f[maxm][maxk],g[maxm][maxk];
int main()
{
	freopen("noise.in","r",stdin);
	freopen("noise.out","w",stdout);
	int n,m,k;
	read(n);read(m);read(k);
	for (int i=1; i<=n; ++i)
	{
		int x;read(x);
		++num[x];
	}

	for (int i=1; i<=m; ++i)
		for (int j=1; j<=k; ++j)
		{
			f[i][0]=C(num[i]);
			for (int l=1; l<=j; ++l)
			{
				ll a=num[i]%(j+1),b=j+1-a;
				f[i][j]=b*C(num[i]/(j+1))+a*C(num[i]/(j+1)+1);
			}
		}

	for (int i=1; i<=m; ++i)
		for (int j=0; j<=k; ++j)
		{
			g[i][j]=g[i-1][j]+f[i][0];
			for (int l=1; l<=j; ++l)
				g[i][j]=min(g[i][j],g[i-1][j-l]+f[i][l]);
		}
	printf("%lld\n",g[m][k]);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/huashuimu2003/article/details/89763575