歌名 - 歌手
0:00

    【NOIP2014模拟11.3】噪音

    题目

    FJ有M个牛棚,编号1至M,刚开始所有牛棚都是空的。FJ有N头牛,编号1至N,这N头牛按照编号从小到大依次排队走进牛棚,每一天只有一头奶牛走进牛棚。第i头奶牛选择走进第p[i]个牛棚。由于奶牛是群体动物,所以每当一头奶牛x进入牛棚y之后,牛棚y里的所有奶牛们都会喊一声“欢迎欢迎,热烈欢迎”,由于声音很大,所以产生噪音,产生噪音的大小等于该牛棚里所有奶牛(包括刚进去的奶牛x在内)的数量。FJ很讨厌噪音,所以FJ决定最多可以使用K次“清空”操作,每次“清空”操作就是选择一个牛棚,把该牛棚里所有奶牛都清理出去,那些奶牛永远消失。“清空”操作只能在噪音产生后执行。现在的问题是:FJ应该选择如何执行“清空”操作,才能使得所有奶牛进入牛棚后所产生的噪音总和最小?

    分析

    发现,每个牛棚产生的噪音互相之间没有关系,所以我们分开做。
    我们知道对于一个牛棚,如果不进行“清空”操作,每次有牛进入的牛棚所产生的噪音将会是1、2、3、4……。
    如果清空一次,当清空操作的位置在二等分点的时候,产生的噪音最小,
    如果清空两次,当清空操作的位置在三等分点的时候,产生的噪音最小,
    ……
    如此类推,
    \(f[i][j]\),表示第i个牛棚,清空j次的最小值,这个直接计算。
    再设\(ff[i]\),表示总共清空i次的最小值。
    类似于背包,一个一个牛棚加入。

    #include <cmath>
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <algorithm>
    #include <queue>
    const long long maxlongint=2147483647;
    const long long mo=1000000007;
    const long long N=505;
    using namespace std;
    long long a[N],f[N][N],n,m,k,ff[N];
    long long val(long long x)
    {
    	return x*(x+1)/2;
    }
    int main()
    {
    	scanf("%lld%lld%lld",&n,&m,&k);
    	for(long long i=1;i<=n;i++)
    	{
    		long long x;
    		scanf("%lld",&x);
    		a[x]++;
    	}
    	for(long long i=1;i<=m;i++)
    	{
    		f[i][0]=val(a[i]);
    		for(long long j=1;j<=k;j++)
    		{
    			long long x=a[i]%(j+1),y=j+1-x;
    			f[i][j]=y*val(a[i]/(j+1))+x*val(a[i]/(j+1)+1);
    		}
    	}
    	for(long long i=1;i<=m;i++)
    	{
    		for(long long j=k;j>=0;j--)
    		{
    			ff[j]+=f[i][0];
    			for(long long l=1;l<=j;l++)
    				ff[j]=min(ff[j],ff[j-l]+f[i][l]);
    		}
    	}
    	printf("%lld",ff[k]);
    }
    
    posted @ 2018-05-22 12:13  无尽的蓝黄  阅读(127)  评论(0编辑  收藏  举报