【JZOJ3920】噪音【dp】

题目:

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


思路:

我们发现,不同牛棚之间是互不相干的,而牛的先后顺序也是无关的。所以可以大胆猜想时间复杂度与n无关
我们记录进入每一个牛棚的牛的数量,设 f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i个牛棚,用了 j j j次清空的最小噪音。
那么再枚举这一个牛棚清除噪音次数 k k k,那么显然把这 k + 1 k+1 k+1段平均分配会最优。
那么维护一下等差数列就好了。
时间复杂度 O ( m k 2 ) O(mk^2) O(mk2)


代码:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;

const int N=110,M=510;
int n,m,t;
ll ans,f[N][M],cnt[N];

int main()
{
	scanf("%d%d%d",&n,&m,&t);
	for (int i=1,x;i<=n;i++)
	{
		scanf("%d",&x);
		cnt[x]++;
	}
	f[0][0]=0;
	for (int i=1;i<=m;i++)
		for (int j=0;j<=t;j++)
		{
			f[i][j]=f[i-1][j]+(1+cnt[i])*cnt[i]/2;
			for (int k=1;k<=min((ll)j,cnt[i]-1);k++)
			{
				ll r=(cnt[i]-1)/(k+1),rr=(cnt[i]-1)%(k+1)+1;
				ll s=(1+r)*r/2*(k+1)+rr*(r+1);
				f[i][j]=min(f[i][j],f[i-1][j-k]+s);
			}
		}
	ans=1e17;
	for (int i=0;i<=t;i++)
		ans=min(ans,f[m][i]);
	printf("%lld",ans);
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值