【AGC035D】Add and Remove(脑洞 DP 分治)

题目链接

大意

给出\(N\)个数的序列,每次操作可以选择连续的三个数,将中间的那个数抽出,将另外两个数的数值加上中间那个数的数值。
一直执行以上操作直到只剩最后两个数,求最后两个数的所有可能的和的最小值。
(\(1\le N\le 18\))

思路

由于\(N\)的奇妙的范围,易想到状态压缩与双向BFS,然而,该题选数顺序对状态的限制太大,故不能。

考虑分治解决,枚举一段区间最后选的数,将其分为两段区间。
设该段区间左端点对答案的贡献为\(X\)次,右端点对答案的贡献为\(Y\)次。
那么在只剩最后选的数,左端点,右端点三个点时,我们选择最后选的数那个点,
那么此时左端点与右端点都分别加上了最后那个数的权值,那么最后那个数对答案的贡献就是\(X+Y\)次。
这样我们就可以分治下去了。

\(F(L,R,X,Y)\)表示左端点为L,右端点为R,左端点贡献为X次,右端点贡献为Y次的区间合并成两个数的最小和。最终所求答案为\(F(1,N,1,1)\)

(小注:这样做的状态数实际上是\((N^2\cdot2^N)\)级别的,转移时间是\(O(N^3\cdot2^N)\)级别的)

代码

不加记忆化能过,用map加了记忆化就不能了。。。
好迷呀。

#include<map>
#include<cstdio>
#include<algorithm>
using namespace std;
#define LL long long
#define pr(a,b) make_pair(a,b)
const int MAXN=20;
const long long INF=5e18;
int N;long long A[MAXN];
long long Solve(int L,int R,LL X,LL Y){
    if(L+1>=R)return 0;
    long long ret=INF;
    for(int i=L+1;i<=R-1;i++)
        ret=min(ret,Solve(L,i,X,X+Y)+Solve(i,R,X+Y,Y)+A[i]*(X+Y));
    return ret;
}
int main(){
    scanf("%d",&N);
    for(int i=1;i<=N;i++)
        scanf("%lld",&A[i]);
    printf("%lld\n",A[1]+A[N]+Solve(1,N,1,1));
}

猜你喜欢

转载自www.cnblogs.com/ftotl/p/11794225.html
今日推荐