HDU 3506 Monkey Party(区间DP)

15 篇文章 1 订阅

Description
n个点1~n按顺序围成一圈,每个点有一个代价,每次可以把相邻的两个点合并成一个点,新点的代价和该次合并的代价均为这两个点的代价之和,问最后合并成一个点的最小花费
Input
第一行一整数n表示点数,之后n个整数a[i]表示第i个点的代价(1<=n<=1000,1<=a[i]<=1000)
Output
输出合并成一个点所需的最小花费
Sample Input
8
5 2 4 7 6 1 3 9
Sample Output
105
Solution
先把前n-1个数复制到第n个数后面将环上问题变成链上问题,dp[i][j]表示将区间[i,j]合并所需最小花费,轻易得到区间DP的转移方程:
dp[i][j]=min{ dp[i][k]+dp[k+1][j]+sum[i][j] },i<=k < j
(表示把该区间分成两部分分别合并然后再把这两部分合并)
sum[i][j]=a[i]+a[i+1]+…+a[j]
该转移是O(n^3)的,还要优化一下,注意到sum[i][j]满足四边形不等式和区间包含关系单调,所以可以用四边形不等式优化
两个定义:
1.w满足四边形不等式等价于对任意i<=ii<=j<=jj有w[i][j]+w[ii][jj]<=w[i][jj]+w[ii][j]
2.w关于区间包含关系单调等价于对任意i<=ii<=jj<=i有w[ii][jj]<=w[i][j]
由于a[i]均为正数,所以sum满足这里两个性质,再给出两个结论
两个结论:若m[i][j]=min( m[i][k-1]+m[k][j]+w[i][j] ),i<=k < j,且m满足上述两条性质,则有
1.m满足四边形不等式
2.令s[i][j]=max{ k | m[i][j]=m[i][k-1]+m[k][j]+w[i][j] },则s单调,即s[i][j]<=s[i][j+1]<=s[i+1][j+1]
故最开始的那个转移方程中k的范围就可以变成s[i][j-1]到s[i+1][j],可以证明这样做的时间复杂度是O(n^2)的
Code

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
#include<queue>
#include<map>
#include<set>
#include<ctime>
using namespace std;
typedef long long ll;
#define INF 0x3f3f3f3f
#define maxn 2222
int n,a[maxn],sum[maxn],dp[maxn][maxn],s[maxn][maxn];
int main()
{
    while(~scanf("%d",&n))
    {
        for(int i=1;i<=n;i++)scanf("%d",&a[i]);
        for(int i=1;i<n;i++)a[i+n]=a[i];
        sum[0]=0;
        for(int i=1;i<2*n;i++)
        {
            s[i][i]=i,dp[i][i]=0;
            sum[i]=sum[i-1]+a[i];
        }
        for(int l=1;l<n;l++)
            for(int i=1;i+l<2*n;i++)
            {
                int j=i+l;
                dp[i][j]=INF;
                for(int k=s[i][j-1];k<=s[i+1][j];k++)
                {
                    int temp=dp[i][k]+dp[k+1][j]+sum[j]-sum[i-1];
                    if(dp[i][j]>=temp)dp[i][j]=temp,s[i][j]=k;
                }
            }
        int ans=dp[1][n];
        for(int i=1;i<n;i++)ans=min(ans,dp[i][i+n-1]); 
        printf("%d\n",ans);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值