description
FJ有M个牛棚,编号1至M,刚开始所有牛棚都是空的。FJ有N头牛,编号1至N,这N头牛按照编号从小到大依次排队走进牛棚,每一天只有一头奶牛走进牛棚。第i头奶牛选择走进第p[i]个牛棚。由于奶牛是群体动物,所以每当一头奶牛x进入牛棚y之后,牛棚y里的所有奶牛们都会喊一声“欢迎欢迎,热烈欢迎”,由于声音很大,所以产生噪音,产生噪音的大小等于该牛棚里所有奶牛(包括刚进去的奶牛x在内)的数量。FJ很讨厌噪音,所以FJ决定最多可以使用K次“清空”操作,每次“清空”操作就是选择一个牛棚,把该牛棚里所有奶牛都清理出去,那些奶牛永远消失。“清空”操作只能在噪音产生后执行。现在的问题是:FJ应该选择如何执行“清空”操作,才能使得所有奶牛进入牛棚后所产生的噪音总和最小?
analysis
\(DP\),考虑预处理辅助数组\(g[i][j]\)表示\(i\)牛棚分割\(j\)次的最小贡献
对于\(num[j]\),当然尽量分割成相等的\(k+1\)份,每一份取等差数列和的贡献
就有\(num[j]\mod(j+1)\)段值为\({num[j]\over{j+1}}+1\),剩下的段都是\({num[j]\over{j+1}}\)
再设\(f[i][j]\)表示前\(i\)个牛棚分割\(j\)次的最小值,\(f[i][j]\)可从\(f[i-1][k]\)转移得到
code
#pragma GCC optimize("O3") #pragma G++ optimize("O3") #include<stdio.h> #include<string.h> #include<algorithm> #define ha 1000000007 #define ll long long #define reg register ll #define fo(i,a,b) for (reg i=a;i<=b;++i) #define fd(i,a,b) for (reg i=a;i>=b;--i) using namespace std; ll f[105][505],g[105][505]; ll num[105]; ll n,m,k,ans=ha; inline ll read() { ll x=0,f=1;char ch=getchar(); while (ch<'0' || '9'<ch){if (ch=='-')f=-1;ch=getchar();} while ('0'<=ch && ch<='9')x=x*10+ch-'0',ch=getchar(); return x*f; } inline ll get(ll x){return x*(x+1)/2;} int main() { freopen("T3.in","r",stdin); n=read(),m=read(),k=read(); fo(i,1,n)++num[read()]; fo(i,1,m) { g[i][0]=get(num[i]); fo(j,1,k) { ll tmp1=num[i]/(j+1),tmp2=num[i]%(j+1),tmp3=j+1-tmp2; g[i][j]=tmp3*get(tmp1)+tmp2*get(tmp1+1); } } fo(i,1,m)fo(j,0,k) { f[i][j]=f[i-1][j]+g[i][0]; fo(l,0,j)f[i][j]=min(f[i][j],f[i-1][j-l]+g[i][l]); } fo(i,0,k)ans=min(ans,f[m][i]); printf("%lld\n",ans); return 0; }
来源:https://www.cnblogs.com/horizonwd/p/12017568.html