先 a i = a i − 1 , b i = b i − 1 \mathfrak{a_i=a_i-1,b_i=b_i-1} ai=ai−1,bi=bi−1
显然数比较少的那一列,每个数都应该属于不同的被操作数集合
考虑上面那一列怎么做。
a b \mathfrak{ab} ab
c d \mathfrak{cd} cd
不拆, ( a + b ) ( c + d ) = a c + b c + a d + b d \mathfrak{(a+b)(c+d)=ac+bc+ad+bd} (a+b)(c+d)=ac+bc+ad+bd
拆, a c + b d \mathfrak{ac+bd} ac+bd。
⋯ \cdots ⋯
可以发现不拆总会比拆答案要大
要用
D
P
\mathcal{DP}
DP相信很容易看出来(
一种自然的思路是考虑把数选入集合。
如果两个序列数的个数相等,当然一个对着一个放进去就是最后的答案
否则就把长的那一列提出来考虑。
也就是考虑长的那一列的第
i
\mathfrak{i}
i个数是否选入第
j
\mathfrak{j}
j个集合。
从集合中选入元素,那么单个集合可以分为选一个或者选多个。
用状态
f
(
i
,
j
)
\mathfrak{f(i,j)}
f(i,j)表示选到长的一列第
i
\mathfrak{i}
i个数,已经有
j
\mathfrak{j}
j个集合,此时的最小答案。
于是
f
(
i
,
j
)
=
m
i
n
{
f
(
i
−
1
,
j
−
1
)
,
f
(
i
−
1
,
j
)
,
f
(
i
,
j
−
1
)
}
+
a
i
b
j
\mathfrak{f(i,j)=min\{f(i-1,j-1),f(i-1,j),f(i,j-1)\}+a_ib_j}
f(i,j)=min{f(i−1,j−1),f(i−1,j),f(i,j−1)}+aibj
其中,
f
(
i
−
1
,
j
−
1
)
\mathfrak{f(i-1,j-1)}
f(i−1,j−1)转移过来代表集合
j
\mathfrak{j}
j里面就只有
i
\mathfrak{i}
i一个数。
而从
f
(
i
−
1
,
j
)
,
f
(
i
,
j
−
1
)
\mathfrak{f(i-1,j),f(i,j-1)}
f(i−1,j),f(i,j−1)则
j
\mathfrak{j}
j里面有多个数。
其中
f
(
i
−
1
,
j
)
\mathfrak{f(i-1,j)}
f(i−1,j)就是集合
j
+
1
\mathfrak{j+1}
j+1还没确定,而
f
(
i
,
j
−
1
)
\mathfrak{f(i,j-1)}
f(i,j−1)是确定了集合
j
\mathfrak{j}
j
这算是把朴素方程
f
(
i
,
j
)
=
m
i
n
{
f
(
x
,
j
−
1
)
+
b
j
∑
k
=
x
+
1
i
a
k
}
\mathfrak{f(i,j)=min\{f(x,j-1)+b_j\sum\limits_{k=x+1}^i a_k}\}
f(i,j)=min{f(x,j−1)+bjk=x+1∑iak}进行了一种懒处理
#include<cstdio>
#include<cstdlib>
#include<iostream>
#include<algorithm>
using namespace std;
int N,M;
long long A[2005]={},B[2005]={};
long long F[2005][2005]={};
int main()
{
cin>>N>>M;
for(int i=1;i<=N;++i)cin>>A[i],--A[i];
for(int i=1;i<=M;++i)cin>>B[i],--B[i];
for(int i=0;i<=N;++i)
for(int j=0;j<=M;++j)
F[i][j]=2147483647147483647ll;
F[0][0]=0;
for(int i=1;i<=N;++i)
for(int j=1;j<=M;++j)
F[i][j]=min(min(F[i][j-1],F[i-1][j]),F[i-1][j-1])+A[i]*B[j];
cout<<F[N][M];
return 0;
}