题目描述 Description
每天,小明和他的马外出,然后他们一边跑一边玩耍。当他们结束的时候,必须带所有的马返回马棚,小明有K个马棚。他把他的马排成一排然后跟随它走向马棚,因为他们非常疲劳,小明不想让他的马做过多的移动。因此他想了一个办法:将马按照顺序放在马棚中,后面的马放的马棚的序号不会大于前面的马放的马棚的序号。而且,他不想他的K个马棚中任何一个空置,也不想任何一匹马在外面。已知共有黑、白两种马,而且它们相处得并不十分融洽。如果有i个白马和j个黑马在一个马棚中,那么这个马棚的不愉快系数将是i*j。所有k个马棚不愉快系数的和就是系数总和。确定一种方法把n匹马放入k个马棚,使得系数总和最小
输入描述 Input Description
输入:在第一行有两个数字:n(1≤n≤500)和k(1≤k≤n)。在接下来的n行是n个数。在这些行中的第i行代表队列中的第i匹马的颜色:1意味着马是黑色的,0意味着马是白色的。
输出描述 Output Description
输出:只输出一个单一的数字,代表系数总和可能达到的最小值
样例输入 Sample Input
6 3
1
1
0
1
0
样例输出 Sample Output
2
和乘积最大有点像,也是一个区间dp,状态可以表示为把前i个数分成j个部分。
然后可以把问题转化为求前面几个数分成j-1个部分,然后再加上最后一部分(其实不就是j部分么)
即f[i][k]=min(f[i][k],f[j][k-1]+anger[j+1][i]);
预先处理出来怒气值。
【代码】
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n,k1;
struct hp{
int x,y;
}s[505];
int f[505][505],anger[505][505],a[505];
int main(){
scanf("%d%d",&n,&k1);
memset(f,127/3,sizeof(f));
for (int i=1;i<=n;++i){
scanf("%d",&a[i]);
s[i].x=s[i-1].x;
s[i].y=s[i-1].y;
if (a[i]==0) s[i].x++;
else s[i].y++;
}
for (int i=1;i<n;++i)
for (int j=i+1;j<=n;++j)
anger[i][j]=(s[j].x-s[i-1].x)*(s[j].y-s[i-1].y);
for (int i=1;i<=n;++i) f[i][1]=anger[1][i];
for (int k=1;k<=k1;++k)
for (int i=k;i<=n;++i)
for (int j=k;j<=i;++j)
f[i][k]=min(f[i][k],f[j][k-1]+anger[j+1][i]);
printf("%d\n",f[n][k1]);
}