zoukankan      html  css  js  c++  java
  • 【洛谷5336】[THUSC2016] 成绩单(区间DP)

    点此看题面

    大致题意: 给你一个序列,让你每次从中取出连续一段数,取完后序列剩下的数将会重新合在一起。设(k)为取数的次数,(min_i,max_i)为第(i)次取的数中的最小值和最大值,求(a imes k+b imessum_{i=1}^k(max_i-min_i)^2)(a,b)为常数)。

    区间(DP)

    一道区间(DP)题。

    考虑设(f_{l,r})表示取完区间([l,r])的最小代价

    显然这不太好转移。

    因此我们再设(g_{l,r,Mn,Mx})表示在区间([l,r])中,还未取走的数中最小值为(Mn)、最大值为(Mx)时的最小代价,来辅助转移。

    注意,此处要先将数给离散化,否则会(MLE)

    先考虑(f)如何从(g)转移得到,显然可以枚举(Mn,Mx)得到:

    [f_{l,r}=g_{l,r,Mn,Mx}+a+b imes(Mx-Mn)^2 ]

    然后再考虑(g)的转移。

    对于(g_{l,r,Mn,Mx}),我们显然可以把区间([l,r])分成两部分,然后有两种转移方式:

    • 把区间划分为左/右端点和其他部分,放在一起取走,得到转移:(g_{l,r,min(Mn,w_l),max(Mx,w_l)}=g_{l+1,r,Mn,Mx}),(g_{l,r,min(Mn,w_r),max(Mx,w_r)}=g_{l,r-1,Mn,Mx})
    • 枚举一个划分点(k),把区间划分为([l,k])([k+1,r])两部分,取走其中([l,k])这一部分,剩余([k+1,r])这一部分,得到转移:(g_{l,r,Mn,Mx}=f_{l,k}+g_{k+1,r,Mn,Mx})

    有了转移方程,接下来就是基础的区间(DP)了。

    代码

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 50
    #define INF 1e9
    #define min(x,y) ((x)<(y)?(x):(y))
    #define max(x,y) ((x)>(y)?(x):(y))
    #define Gmin(x,y) (x>(y)&&(x=(y)))
    using namespace std;
    int n,a,b,dc,w[N+5],dv[N+5],f[N+5][N+5],g[N+5][N+5][N+5][N+5];
    int main()
    {
    	RI i,l,r,k,Mn,Mx;for(scanf("%d%d%d",&n,&a,&b),i=1;i<=n;++i) scanf("%d",w+i),dv[i]=w[i];
    	#define GV(x) (lower_bound(dv+1,dv+dc+1,x)-dv)
    	for(sort(dv+1,dv+n+1),dc=unique(dv+1,dv+n+1)-dv-1,i=1;i<=n;++i) w[i]=GV(w[i]);//离散化
    	for(l=1;l<=n;++l) for(r=l;r<=n;++r)//初始化成INF
    		for(f[l][r]=INF,Mn=1;Mn<=dc;++Mn) for(Mx=Mn;Mx<=dc;++Mx) g[l][r][Mn][Mx]=INF;
    	for(i=1;i<=n;++i) f[i][i]=a,g[i][i][w[i]][w[i]]=0;//初始化边界
    	for(i=2;i<=n;++i) for(l=1,r=i;r<=n;++l,++r)//先枚举区间长度,再枚举区间
    	{
    		for(Mn=1;Mn<=dc;++Mn) for(Mx=Mn;Mx<=dc;++Mx)//把区间划分为左/右端点和其他部分
    			Gmin(g[l][r][min(Mn,w[l])][max(Mx,w[l])],g[l+1][r][Mn][Mx]),
    			Gmin(g[l][r][min(Mn,w[r])][max(Mx,w[r])],g[l][r-1][Mn][Mx]);
    		for(Mn=1;Mn<=dc;++Mn) for(Mx=Mn;Mx<=dc;++Mx)//枚举一个划分点把区间划分为两部分
    			for(k=l;k^r;++k) Gmin(g[l][r][Mn][Mx],f[l][k]+g[k+1][r][Mn][Mx]);
    		for(Mn=1;Mn<=dc;++Mn) for(Mx=Mn;Mx<=dc;++Mx)//通过g求出f
    			Gmin(f[l][r],g[l][r][Mn][Mx]+a+b*(dv[Mx]-dv[Mn])*(dv[Mx]-dv[Mn]));
    	}printf("%d",f[1][n]),0;//输出答案
    }
    
  • 相关阅读:
    Systemd 入门教程:实战篇
    Systemd 入门教程:命令篇
    awk详解
    yum Multilib version problems
    Oracle ORA 12541 报错解决过程
    创建表空间
    大文件按行分离脚本
    heartbeat安装与配置
    Keepalived安装配置
    速查表的名称和建表语句
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/Luogu5336.html
Copyright © 2011-2022 走看看