1. 程式人生 > >[Luogu1846] [NOIP2012模擬10.22] 遊戲 [dp]

[Luogu1846] [NOIP2012模擬10.22] 遊戲 [dp]

link

ai=ai1,bi=bi1\mathfrak{a_i=a_i-1,b_i=b_i-1}

顯然數比較少的那一列,每個數都應該屬於不同的被運算元集合 考慮上面那一列怎麼做。

ab\mathfrak{ab} cd\mathfrak{cd} 不拆,(a+b)(c+d)=ac+bc+ad+bd\mathfrak{(a+b)(c+d)=ac+bc+ad+bd} 拆,ac+bd\mathfrak{ac+bd}\cdots

可以發現不拆總會比拆答案要大

要用DP\mathcal{DP}

相信很容易看出來( 一種自然的思路是考慮把數選入集合。 如果兩個序列數的個數相等,當然一個對著一個放進去就是最後的答案 否則就把長的那一列提出來考慮。 也就是考慮長的那一列的第i\mathfrak{i}個數是否選入第j\mathfrak{j}個集合。 從集合中選入元素,那麼單個集合可以分為選一個或者選多個。 用狀態f(i,j)\mathfrak{f(i,j)}表示選到長的一列第i\mathfrak{i}個數,已經有j\mathfrak{j}個集合,此時的最小答案。 於是f(i,j)=min{f(i1,j1),f(i1,j),f(i,j1)}+aibj\mathfrak{f(i,j)=min\{f(i-1,j-1),f(i-1,j),f(i,j-1)\}+a_ib_j}
其中,f(i1,j1)\mathfrak{f(i-1,j-1)}轉移過來代表集合j\mathfrak{j}裡面就只有i\mathfrak{i}一個數。 而從f(i1,j),f(i,j1)\mathfrak{f(i-1,j),f(i,j-1)}j\mathfrak{j}裡面有多個數。 其中f(i1,j)\mathfrak{f(i-1,j)}就是集合j+1\mathfrak{j+1}還沒確定,而f(i,j1)
\mathfrak{f(i,j-1)}
是確定了集合j\mathfrak{j} 這算是把樸素方程f(i,j)=min{f(x,j1)+bjk=x+1iak}\mathfrak{f(i,j)=min\{f(x,j-1)+b_j\sum\limits_{k=x+1}^i a_k}\}進行了一種懶處理

#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;
}