题意简述
以下只考虑红色值。有
n
n
n个色块,每个色块占
c
n
t
i
cnt_i
cnti个像素,红色值强度为
v
a
l
i
val_i
vali。选择
k
k
k个整数
v
1
,
v
2
⋯
v
k
v_1,v_2\cdots v_k
v1,v2⋯vk,定义"平方误差"为
∑
i
=
1
n
c
n
t
i
×
m
i
n
j
=
1
k
{
(
v
a
l
i
−
v
j
)
2
}
\sum\limits_{i=1}^{n}cnt_i\times min_{j=1}^{k}\{(val_i-v_j)^2\}
i=1∑ncnti×minj=1k{(vali−vj)2},(意会理解:对于第
i
i
i个色块,选择一个
v
j
v_j
vj满足
(
v
a
l
i
−
v
j
)
2
(val_i-v_j)^2
(vali−vj)2最小,然后用这个最小值乘上
c
n
t
i
cnt_i
cnti,把每个色块得到的这些和加起来)。
合理安排使得"平方误差"最小。
数据
输入
第一行两个正整数
n
,
k
n,k
n,k
接下来
n
n
n行,每行两个数
v
a
l
i
val_i
vali和
c
n
t
i
cnt_i
cnti。保证
v
a
l
i
val_i
vali 升序
输出
输出最小的"平方误差"。
样例
输入
2 1
50 20000
150 10000
输出
66670000
输入
2 2
50 20000
150 10000
输出
0
输入
4 2
0 30000
25 30000
50 30000
255 30000
输出
37500000
思路
显然是 D P DP DP吧。。。像这种题,题做多了,一样就能看出来应该设 d p [ i ] [ j ] dp[i][j] dp[i][j]为考虑前 i i i个色块,选择了 j j j个整数。显然,答案就是 d p [ n ] [ k ] dp[n][k] dp[n][k]。那么,如何转移呢?
对于 d p [ i ] [ j ] dp[i][j] dp[i][j]我们选择一个断点 k k kk kk(为了不和 k k k重名),那么此时就要用 d p [ k k ] [ j − 1 ] dp[kk][j-1] dp[kk][j−1]+( k k + 1 kk+1 kk+1到 i i i选择某整数的最优解)的值来更新 d p [ i ] [ j ] dp[i][j] dp[i][j]。现在我们发现关键了,( k k + 1 kk+1 kk+1到 i i i选择某整数的最优解)这个值怎么求?
如果我们要暴力求的话,那么我们的
d
p
dp
dp就是四次方的
d
p
dp
dp,就会
F
F
T
(
f
a
s
t
−
f
a
s
t
−
T
L
E
)
FFT(fast-fast-TLE)
FFT(fast−fast−TLE)了。所以我们考虑 打表 预处理。首先枚举转移到了哪个点
k
k
kk
kk(也是为了不和
k
k
k重名),然后枚举所有区间,一边枚举一遍加上和,然后更新最小值。预处理就是
O
(
n
3
)
O(n^3)
O(n3)的了。这样,我们的
d
p
dp
dp也就是
O
(
n
3
)
O(n^3)
O(n3)的了,能过。
代码:
#include<bits/stdc++.h>
using namespace std;
namespace Flandle_Scarlet
{
#define N 300
#define int long long
int n,k;
struct col
{
int val,cnt;
}a[N];
void Input()
{
scanf("%lld%lld",&n,&k);
if (k>=n)
{
puts("0\n");
exit(0);
}
//k>=n的情况,直接选择一些整数和n个色块重合,答案就是0了。这个特判掉
for(int i=1;i<=n;++i)
{
int val,cnt;scanf("%lld%lld",&val,&cnt);
a[i]=(col){val,cnt};
}
}
int dp[N][N];
int dis[N][N];
//dis[l][r]表示l到r整体都选择某个点的最小代价
int sqr(int x){return x*x;}
void DP()
{
memset(dis,0x3f,sizeof(dis));
for(int kk=0;kk<=256;++kk)
//枚举断点kk
{
for(int i=1;i<=n;++i)
{
int ans=0;
for(int j=i;j<=n;++j)
{
ans+=sqr(a[j].val-kk)*a[j].cnt;
//叠加的
dis[i][j]=min(dis[i][j],ans);
//更新最小值
}
}
}
memset(dp,0x3f,sizeof(dp));
dp[0][0]=0;//注意边界!!!
for(int i=1;i<=n;++i)
{
for(int j=1;j<=k and j<=i;++j)
{
for(int kk=0;kk<i;++kk)
{
dp[i][j]=min(dp[i][j],dp[kk][j-1]+dis[kk+1][i]);
//这个转移就很显然了。。。
}
}
}
printf("%lld\n",dp[n][k]);
}
void Main()
{
Input();
DP();
}
};
main()
{
Flandle_Scarlet::Main();
return 0;
}