HDU 3480
【题目描述】
Little D is really interested in the theorem of sets recently. There’s a problem that confused him a long time.
Let T be a set of integers. Let the MIN be the minimum integer in T and MAX be the maximum, then the cost of set T if defined as (MAX – MIN)^2. Now given an integer set S, we want to find out M subsets S1, S2, …, SM of S, such that
and the total cost of each subset is minimal.
题目大意:
将n个数(可以不按题目所给的顺序)划分为m个区间,每个区间的代价是该区间内最大值与最小值差的平方,试求最小代价;
【解题报告】
这道题的动态转移方程不难写出:
(先期排一次序)
dp[i][k]=min(dp[j][k-1]+(a[i]-a[j+1])^2);
//其中dp[i][k]表示前i个数分为k个区间时的最优解,1<=j<i
这样一个转移方程我们有两种思路,一种就是斜率优化,另一种就是这里讲的四边形优化(后者比较好写)。
要采用四边形优化我们需要dp数组和其后的(a[i]-a[j+1])^2满足两个性质,即
对于 i≤i'<j≤j',有 w(i',j)≤w(i,j')
对于 i≤i'<j≤j',有 w(i,j)+w(i',j')≤w(i',j)+w(i,j')
我们可以证明这道题是满足这两个性质的。
那么这样就可以得出一个结论:
动态转移方程中的j满足 :
s(i,j)≤s(i,j+1)==j≤s(i+1,j+1)
无疑,这样远远缩小了我们需要枚举的j的次数,其优越性就体现在这个地方。
下面我们看代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
const int N=10000;
const int M=5000;
int T,Case;
int n,m;
int a[N+5],dp[N+5][M+5],s[N+5][M+5];
int main()
{
for(scanf("%d",&T);T;--T)
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
sort(a+1,a+1+n);//先排序
for(int i=1;i<=n;i++)
dp[i][1]=(a[i]-a[1])*(a[i]-a[1]),s[i][1]=1;//赋初值
for(int k=2;k<=m;k++)//枚举区间个数
{
s[n+1][k]=n-1;
for(int i=n;i>=k;i--)//枚举起点,dp数组更新需要依赖前面的值因此从后向前
{
dp[i][k]=dp[k-1][k-1]+(a[i]-a[k])*(a[i]-a[k]);
s[i][k]=k;
for(int j=s[i][k-1];j<=s[i+1][k];j++)//枚举“决策点”
{
int t=dp[j][k-1]+(a[i]-a[j+1])*(a[i]-a[j+1]);
if(t<dp[i][k])
{
dp[i][k]=t;
s[i][k]=j;//更新s数组
}
}
}
}
printf("Case %d:%d \n",++Case,dp[n][m]);
}
return 0;
}
以上