HDU 3506 区间DP 四边形不等式

HDU 3506 区间DP 四边形不等式优化

题意

这个题目和石子合并差不多,区别在于这个是个环。

输入第一行是一个数n,表示一共有多少个数,接下来有n个数,来表示对应的权值。

解题思路

对于环形,我们可以使用长度为二倍的数组来进行保存,1到n保存对应数的权值,n+1到2*n保存的也是1到n对应的权值,这样我们在进行处理的时候就可以处理到最后一个元素和第一个元素了。

数组dp[i][j]表示区间范围为[i,j]内的最优值。很容易想到,我们需要三重的循环来进行解决这个题目,如下:

for(int len = 1; len < n; i++)
    for(int i=1; i+len-1 <= 2*n; i++){
        int j=i+len-1;
        for(int k=i; k<=j; k++)//寻找i到j中的最优分割点
            if(dp[i][j] > dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1])
                dp[i][j] = dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1];
    }

但是这样的话,复杂度就是O(N^3),对于这个题目来说是不行的,因此需要其他的方式了进行优化,于是四边形不等式优化就出来了。

首先说明一下使用这个优化后的复杂度在O(N^2)。

四边形不等式的具体证明,https://blog.csdn.net/qq_41695941/article/details/83025188

四边形不等式 是在区间上进行的,一个口诀是 交叉小于等于包含

概括来说,首先需要cost数组符合四边形不等式,这里的cost数组存储的是最初的耗费,(也可以不考虑这一步,直接进入下一步)。

然后需要dp数组符合四边形不等式(dp是我们定义的某种具有特殊含义的数组)

满足上面的条件后,我们需要使用一个二维数组来进行存储对应区间上的最优分割点,假设这个数组为ss[i][j],代表的含义是 在区间[i,j]内的最优分割点的值。

ss数组需要满足ss[i][j-1] <= ss[i][j] <= ss[i+1][j]。这样我们的每次的遍历就是在ss[i][j-1] 到 ss[i+1][j]之间进行遍历。

虽然形式上还是三层的for循环,但是复杂度已经减少了,这里我不会分析,哈哈,还是太菜了。

代码实现

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<sstream>
typedef long long ll;
using namespace std;
const double esp=1e-6;
const int inf=0x3f3f3f3f;
const int MAXN=1e3+7;
int n;
int dp[MAXN<<1][MAXN<<1], ss[MAXN<<1][MAXN<<1];
int wt[MAXN<<1], sum[MAXN<<1]; 
int main()
{
	while(scanf("%d", &n)!=EOF){
		sum[0] = 0;
		memset(dp, inf, sizeof(dp));
		for(int i=1; i<=n; i++){
			scanf("%d", &wt[i]);
			wt[i+n] = wt[i];
		}
		for(int i=1; i<=(n<<1); i++){
			ss[i][i] = i;
			dp[i][i] = 0;
			sum[i] = sum[i-1] + wt[i];
		}
		for(int len = 1; len <= n; len++)
			for(int i=1; i+len-1<=2*n; i++)
			{
				int j = i + len - 1;
				for(int k=ss[i][j-1]; k<=ss[i+1][j]; k++)
					if(dp[i][j] > dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1])
					{
						dp[i][j] = dp[i][k] + dp[k+1][j] + sum[j] - sum[i-1];
						ss[i][j] = k;
					}
			}
		int ans = inf;
		for(int i=1; i<=n; i++)
			ans = min(ans, dp[i][i+n-1]);
		printf("%d
", ans);
	}

	return 0;
}
原文地址:https://www.cnblogs.com/alking1001/p/13225892.html