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


    题目链接

    大意

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

    思路

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

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

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

    (小注:这样做的状态数实际上是((N^2cdot2^N))级别的,转移时间是(O(N^3cdot2^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
    ",A[1]+A[N]+Solve(1,N,1,1));
    }
    
  • 相关阅读:
    Spark Interaction(特征交互-笛卡尔转换)
    Spark DCT 离散余弦变换
    Spark polynomialExpansion 多项式扩展
    Spark PCA
    Spark n-gram模型
    Spark OneHotEncoder
    Spark 逻辑回归LogisticRegression
    查看macOS下正在使用的zsh
    Neovim中NERDTree等多处cursorline不高亮
    让pip使用python3而不是python2
  • 原文地址:https://www.cnblogs.com/ftotl/p/11794225.html
Copyright © 2020-2023  润新知