HDU 3506 Monkey Party(区间DP)

来源:互联网 发布:拳皇14出招优化补丁 编辑:程序博客网 时间:2024/06/10 10:17

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 2222int 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;}
原创粉丝点击