音乐播放器
chu_K's blog
 
文章 标签
22 9

Powered by Gridea | Theme: Fog
载入天数...
载入时分秒...
御风飞行中...

【OI】洛谷题解P6418 [COCI2014-2015#1] ZABAVA

Sol

看到题目,我们首先想到dp,其中 f[i][j]f[i][j] 表示将前 ii 栋楼中的学
生进行 jj 次操作所得到的最小吵闹值。那么最终的答案就是 f[m][k]f[m][k]

dp方程也很好推: f[i][j]=min(f[i][j],f[i1][k]+sol(i,j,kk))f[i][j]=min(f[i][j],f[i-1][k]+sol(i,j,kk))

dp部分代码:

    for (i=1; i<=m; i++)
      for (j=0; j<=k; j++)
         for (kk=0; kk<=j; kk++)
            f[i][j]=min(f[i][j],f[i-1][kk]+cmf(i,j,k));

那么本题的一个难点就是怎样进行操作是最优的,观察样例我们不难发现,将学生尽量均分是最优的。简化一下题目,进行 kk 次操作,其实就是将学生分为 k+1k+1 分。不难发现均分后的最小值肯定是 nk+1\frac{n}{k+1} , 那么最大值也就是 1+nk+11+\frac{n}{k+1} 这样计算起来就很简单了。

Tips:
若使用子程序进行计算千万别忘了开 long long 否则会WA两个点。

Code

#include <iostream>
#include <cstdio>
#include <algorithm>
#define int long long//别忘了
using namespace std;
int n,m,k,i,j,x,kk,kkk,num,sum,mon,a[555],f[555][555];

int cmf(int i, int j, int kk)
{
        kkk=j-kk;
        mon=a[i] / (kkk+1);//最小值
        sum=(mon+1) * (kkk+1) - a[i];
        num=(kkk+1) - sum;
        sum=sum * mon * (mon+1) / 2 + num * (mon+1) * (mon+2) / 2;
        return sum;
}//计算sol

signed main()
{
        cin >> n >> m >> k;
        for (i=1; i<=n; i++)
        {
                cin >> x;
                a[x]++;
        }

        for (i=1; i<=m; i++)
                for (j=0; j<=k; j++)
                        f[i][j]=0x3f3f3f3f;//预处理

        for (i=1; i<=m; i++)
                for (j=0; j<=k; j++)
                        for (kk=0; kk<=j; kk++)
                                f[i][j]=min(f[i][j],f[i-1][kk]+cmf(i,j,kk));//dp
        cout << f[m][k];
}