3个区间DP总结

一、

题目:石子归并CSU - 1592

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>  
#include<vector>
#include<queue>
#include<set>																//自动从小到大排序,且没有重复 

using namespace std;

const int maxn = 100 + 5;
const int INF = 1e9;

int n;
int a[maxn];
int sum[maxn];															//记录前缀和,方便算得分 
int dp[maxn][maxn];														


/*
	区间dp 
	对比题J 
*/

int main(){
	int t;
	scanf("%d", &t);
	while(t--){
		scanf("%d", &n);
		for(int i=1; i<=n; i++){
			scanf("%d", &a[i]);
		}

		
		fill(dp[0], dp[0]+maxn*maxn, INF);
		sum[0] = 0;
		for(int i=1; i<=n; i++){
			dp[i][i] = 0;
			sum[i] = sum[i-1] + a[i];
		}

	
		//从小到大枚举区间长度 
		
		//区间[1, n] 
		int end;
		for(int len=1; len<n; len++){										//区间长度 从1到n-1 
			for(int s=1; s<=n-len; s++){									//start起始下标    s+len<=n :因为len>=1,所以i最多会被枚举到 n-1 
				end = s+len;												//结束下标   		end = s+len
				for(int k=s; k<=end-1; k++){								//中间点下标       k必须从s起,不然会WA 
					dp[s][end] = min(dp[s][end], dp[s][k] + dp[k+1][end] + sum[end] - sum[s-1]);		//sum[end]-sum[s-1]是合并本次区间的得分 
				}
			}
		}
			
		cout << dp[1][n] << endl;		
	}
	
	return 0;
} 

二、

题目:Multiplication Puzzle POJ - 1651

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>  
#include<vector>
#include<queue>
#include<set>																//自动从小到大排序,且没有重复 

using namespace std;

const int maxn = 100 + 5;
const int INF = 1e9;

/*
	区间dp,可转化为矩阵链乘
	对比题I
	
*/

int n;
int a[maxn];
int dp[maxn][maxn]; 


int main(){
	scanf("%d", &n);
	for(int i=0; i<n; i++){
		scanf("%d", &a[i]);	
	} 
	memset(dp, 0, sizeof(dp));
	for(int i=0; i+2<n; i++){
		dp[i][i+2] = a[i] * a[i+1] * a[i+2];						//初始长度为2时的乘积 ,此时包含3个数 
	}
	
	
	//对比着i题的区间dp看
	
	//区间 [0, n-1] 
	int e;
	for(int len=3; len<n; len++){									//长度从3开始枚举,此时包含四个数 
		for(int s=0; s+len<n; s++){									//起始位置 
			e = len + s;											//终点 
			for(int k=s+1; k<e; k++){								//枚举断点 ,删除的不是k,而是中间除了s, k, end以外的点 ,本轮选取k 
				if(dp[s][e]==0) dp[s][e] = dp[s][k] + dp[k][e] + a[s] * a[k] * a[e];					//还没有被计算过 
				else dp[s][e] = min(dp[s][e], dp[s][k] + dp[k][e] + a[s] * a[k] * a[e]);
			}
		}
	}
	
	cout << dp[0][n-1] << endl;
	
	return 0;
} 

三、

题目:Zuma CodeForces - 607B

代码

#include <iostream>
#include<cstdio>
#include<algorithm>

using namespace std;

const int INF = 1e9;
const int maxn = 500 + 10;


int n;
int a[maxn];
int dp[maxn][maxn];


int main(){
    scanf("%d", &n);
    for(int i=0; i<n; i++) scanf("%d", &a[i]);

    //初始化
    for(int i=0; i<n; i++){
        for(int j=0; j<n; j++){
            dp[i][j] = INF;
        }
    }
    for(int i=0; i<n; i++) dp[i][i] = 1;
    for(int i=0; i<n-1; i++){
        if(a[i] == a[i+1]) dp[i][i+1] = 1;
        else dp[i][i+1] = 2;
    }


    //区间[0, n-1]
    int end;
    for(int len=2; len<n; len++){
        for(int s=0; s+len<n; s++){
            end =  s + len;

            //按区间长度从小到大来的,这步等于缩小了区间长度,所以肯定在前面已经计算过了
            if(a[s] == a[end]) dp[s][end] = dp[s+1][end-1];

            for(int k=s; k<end; k++){
                dp[s][end] = min(dp[s][end], dp[s][k] + dp[k+1][end]);
            }

        }


    }
    cout << dp[0][n-1] << endl;

    return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值