「
「
「动态规划
」
」
」第
2
2
2章 区间
D
P
DP
DP
(
(
(后
2
2
2题
)
)
)
目录:
C.消除木块
D.棋盘分割
C . C. C. 例题 3 3 3 消除木块
分析:
区间
d
p
dp
dp
c
o
l
k
col_k
colk 表示
k
k
k区间的颜色
l
e
n
k
len_k
lenk表示
k
k
k区间的长度
先预处理这些
f
l
,
r
,
k
f_{l,r,k}
fl,r,k表示 消除
[
l
,
r
]
[l,r]
[l,r]区间 在
r
r
r后有
k
k
k个与
r
r
r颜色相同方块的最大分数值
首先考虑直接消除
[
l
,
r
]
[l,r]
[l,r]区间 那就是
f
l
,
r
−
1
,
k
+
f_{l,r-1,k}+
fl,r−1,k+当前
r
r
r的分数
其次就是区间
d
p
dp
dp 区间分别为
f
l
,
i
,
l
e
n
[
r
]
+
k
,
f
i
+
1
,
r
−
1
,
0
f_{l,i,len[r]+k},f_{i+1,r-1,0}
fl,i,len[r]+k,fi+1,r−1,0
但是要颜色相同 这样就可以消除
[
i
+
1
,
r
−
1
]
[i+1,r-1]
[i+1,r−1] 再计算
[
l
,
i
]
[l,i]
[l,i]的
l
=
r
l=r
l=r 就直接计算当前的分数
记搜实现
CODE:
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
const int N=255;
int T,n,col[N],len[N],f[N][N][N<<1],r;
int dfs(int l,int r,int k)
{
if(f[l][r][k]) return f[l][r][k];
if(l==r) return (len[r]+k)*(len[r]+k);
f[l][r][k]=dfs(l,r-1,0)+(len[r]+k)*(len[r]+k);
for(int i=l;i<r-1;i++)
if(col[i]==col[r])
f[l][r][k]=max(f[l][r][k],dfs(l,i,len[r]+k)+dfs(i+1,r-1,0));
return f[l][r][k];
}
int main(){
scanf("%d",&T);
int E=T;
while(T--)
{
scanf("%d",&n);
int ans=0;
memset(len,0,sizeof(len));
memset(col,0,sizeof(col));
memset(f,0,sizeof(f));
int k=0;r=-1;
for(int i=1;i<=n;i++)
{
int x;
scanf("%d",&x);
if(x==r) len[k]++;
else
{
col[++k]=x;
len[k]++;
}
r=x;
}
ans=dfs(1,k,0);
printf("Case %d: %d\n",E-T,ans);
}
return 0;
}
D . D. D. 例题 4 4 4 棋盘分割
y
b
t
o
j
ybtoj
ybtoj上的题面有问题 先放
l
u
o
g
u
luogu
luogu的
洛谷
l
i
n
k
link
link
分析:
∑
i
=
1
n
(
x
i
−
x
ˉ
)
n
\sqrt{\frac{\sum_{i=1}^n(x_i-\bar{x})}{n}}
n∑i=1n(xi−xˉ)最小 那就是
∑
i
=
1
n
(
x
i
−
x
ˉ
)
n
\frac{\sum_{i=1}^n(x_i-\bar{x})}{n}
n∑i=1n(xi−xˉ)最小
把它展开 就是
∑
i
=
1
n
(
x
i
2
−
2
x
i
2
x
ˉ
+
x
ˉ
2
)
n
=
∑
i
=
1
n
x
i
2
n
−
x
ˉ
2
\frac{\sum_{i=1}^n(x_i^2-2x_i^2\bar{x}+\bar{x}^2)}{n}=\frac{\sum_{i=1}^nx_i^2}{n}-\bar{x}^2
n∑i=1n(xi2−2xi2xˉ+xˉ2)=n∑i=1nxi2−xˉ2
x
ˉ
=
∑
i
=
1
8
∑
j
=
1
8
a
i
,
j
n
\bar{x}=\frac{\sum_{i=1}^8\sum_{j=1}^8a_{i,j}}{n}
xˉ=n∑i=18∑j=18ai,j
所以
x
ˉ
2
\bar{x}^2
xˉ2是定值 那满足
∑
i
=
1
n
x
i
2
\sum_{i=1}^nx_i^2
∑i=1nxi2最小就好了
后面的就是 2 2 2维区间 d p dp dp 分别划分 [ x 1 , x 2 ] [x1,x2] [x1,x2] [ y 1 , y 2 ] [y1,y2] [y1,y2]
f
x
1
,
y
1
,
x
2
,
y
2
,
k
f_{x1,y1,x2,y2,k}
fx1,y1,x2,y2,k表示左上角为
(
x
1
,
y
1
)
(x1,y1)
(x1,y1) 右下角为
(
x
2
,
y
2
)
(x2,y2)
(x2,y2)的矩阵划分成
k
k
k个子矩阵的最小
σ
2
\sigma^2
σ2
需要
2
2
2维前缀和
s
i
,
j
=
∑
k
=
1
i
∑
l
=
1
j
a
k
,
l
s_{i,j}=\sum_{k=1}^i\sum_{l=1}^ja_{k,l}
si,j=∑k=1i∑l=1jak,l
每次转移需要计算子矩阵和 令函数
v
a
l
(
x
1
,
y
1
,
x
2
,
y
2
)
val(x1,y1,x2,y2)
val(x1,y1,x2,y2)表示左上角
(
x
1
,
y
1
)
(x1,y1)
(x1,y1) 右下角
(
x
2
,
y
2
)
(x2,y2)
(x2,y2)的矩阵和
因为
a
n
s
=
m
i
n
{
∑
i
=
1
n
x
i
2
n
−
x
ˉ
2
}
ans=min\{\frac{\sum_{i=1}^nx_i^2}{n}-\bar{x}^2\}
ans=min{n∑i=1nxi2−xˉ2}
做完
m
i
n
{
f
x
1
,
y
1
,
x
2
,
y
2
,
k
}
min\{f_{x1,y1,x2,y2,k}\}
min{fx1,y1,x2,y2,k}后
a
n
s
ans
ans就等于
f
1
,
1
,
8
,
8
,
n
−
x
ˉ
2
\sqrt{f_{1,1,8,8,n}-\bar{x}^2}
f1,1,8,8,n−xˉ2
数据只有 8 × 8 , n < = 15 8\times8,n<=15 8×8,n<=15 就直接 5 5 5循环 d p dp dp了
CODE:
#include<cmath>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<iostream>
using namespace std;
typedef long long ll;
const int N=1e1-1;
int n,a[N][N];
double f[N][N][N][N][N<<1];
double val(int x1,int y1,int x2,int y2)
{
double res=a[x2][y2]-a[x2][y1-1]-a[x1-1][y2]+a[x1-1][y1-1];
return res*res/n;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=8;i++)
for(int j=1;j<=8;j++)
{
scanf("%d",&a[i][j]);
a[i][j]+=a[i-1][j]+a[i][j-1]-a[i-1][j-1];
}
double X=(double)a[8][8]/n;
for(int k=1;k<=n;k++)
for(int x1=1;x1<=8;x1++)
for(int y1=1;y1<=8;y1++)
for(int x2=1;x2<=8;x2++)
for(int y2=1;y2<=8;y2++)
{
f[x1][y1][x2][y2][k]=1e9;
if(k==1)
{
f[x1][y1][x2][y2][k]=val(x1,y1,x2,y2);
continue;
}
for(int i=x1;i<x2;i++)
{
f[x1][y1][x2][y2][k]=min(f[x1][y1][x2][y2][k],f[x1][y1][i][y2][k-1]+val(i+1,y1,x2,y2));
f[x1][y1][x2][y2][k]=min(f[x1][y1][x2][y2][k],f[i+1][y1][x2][y2][k-1]+val(x1,y1,i,y2));
}
for(int i=y1;i<y2;i++)
{
f[x1][y1][x2][y2][k]=min(f[x1][y1][x2][y2][k],f[x1][y1][x2][i][k-1]+val(x1,i+1,x2,y2));
f[x1][y1][x2][y2][k]=min(f[x1][y1][x2][y2][k],f[x1][i+1][x2][y2][k-1]+val(x1,y1,x2,i));
}
}
printf("%.3lf",sqrt(f[1][1][8][8][n]-X*X));
return 0;
}