zoukankan      html  css  js  c++  java
  • 【BZOJ4897】成绩单(THUSC2016)-玄幻区间DP

    测试地址:成绩单
    做法:本题需要用到区间DP。
    容易想到每次取的都是一个子序列。直觉上想到的一个状态定义是,令f(i,j)为删掉区间[i,j]中所有数的最小代价,但我们发现这没法转移,又注意到转移和所取的子序列中最大数和最小数有关,那么便有了如下状态定义和状态转移方程:
    f(i,j,l,r)为删掉区间[i,j]中的一部分数,使得剩下的数都在[l,r]范围内的最小代价,g(i,j)为删掉区间[i,j]中所有数的代价,则有:
    f(i,j,l,r)=min(g(tl,tr),f(i,k,l,r)+f(k+1,j,l,r)(ik<j))
    其中tl,tr为区间[l,r]中从左边/右边开始数第一个不在[l,r]内的数的位置。这个方程通过一种玄幻的方式枚举了所有可能是最优的方案,可以感性理解一下……
    然后我们有:
    g(i,j)=min(f(i,j,l,r)+a+b(rl)2)
    这个方程就很显然了。那么我们把所有数离散化后做DP,就可以解决这一题了,时间复杂度为O(n5)
    (这题真的好难啊……为什么还有dalao说是DP入门题啊……可能是国家队入门吧)
    以下是本人代码:

    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const ll inf=1000000000ll*1000000000ll;
    int n,now[55];
    ll a,b,pos[55],f[55][55][55][55],g[55][55];
    struct forsort
    {
        int id;
        ll val;
    }F[100];
    
    bool cmp(forsort a,forsort b)
    {
        return a.val<b.val;
    }
    
    int main()
    {
        scanf("%d%lld%lld",&n,&a,&b);
        for(int i=1;i<=n;i++)
        {
            scanf("%lld",&F[i].val);
            F[i].id=i;
        }
    
        sort(F+1,F+n+1,cmp);
        int tot=0;
        for(int i=1;i<=n;i++)
        {
            if (i==1||F[i].val!=F[i-1].val) pos[++tot]=F[i].val;
            now[F[i].id]=tot;
        }
    
        for(int i=1;i<=n;i++)
        {
            g[i][i]=a;
            for(int l=1;l<=tot;l++)
                for(int r=l;r<=tot;r++)
                {
                    if (now[i]>=l&&now[i]<=r) f[i][i][l][r]=0;
                    else f[i][i][l][r]=a;
                }
        }
        for(int len=2;len<=n;len++)
            for(int i=1;i+len-1<=n;i++)
            {
                int j=i+len-1,maxv=0,minv=tot+1;
                for(int x=i;x<=j;x++)
                    maxv=max(maxv,now[x]),minv=min(minv,now[x]);
                g[i][j]=a+b*(pos[maxv]-pos[minv])*(pos[maxv]-pos[minv]);
                for(int l=1;l<=tot;l++)
                    for(int r=l;r<=tot;r++)
                    {
                        int tl=i-1,tr=j+1;
                        for(int k=i;k<=j;k++)
                        {
                            if (now[k]>=l&&now[k]<=r) tl=k;
                            else break;
                        }
                        for(int k=j;k>=i;k--)
                        {
                            if (now[k]>=l&&now[k]<=r) tr=k;
                            else break;
                        }
                        if (tl>=tr) f[i][j][l][r]=0;
                        else f[i][j][l][r]=g[tl+1][tr-1];
                        for(int k=i;k<j;k++)
                            f[i][j][l][r]=min(f[i][j][l][r],f[i][k][l][r]+f[k+1][j][l][r]);
                    }
                for(int l=1;l<=tot;l++)
                    for(int r=l;r<=tot;r++)
                        g[i][j]=min(g[i][j],f[i][j][l][r]+a+b*(pos[r]-pos[l])*(pos[r]-pos[l]));
            }
        printf("%lld",g[1][n]);
    
        return 0;
    }
  • 相关阅读:
    TrieTree的学习
    单调队列(monotonic queue)列与单调栈的学习
    507. Perfect Number
    157. Read N Characters Given Read4
    nsexec
    nsenter
    setjmp
    runc 测试
    cgo setns + libcontainer nsexec
    前端 导出为Excel 数据源为table表格 并且table中含有图片
  • 原文地址:https://www.cnblogs.com/Maxwei-wzj/p/9793358.html
Copyright © 2011-2022 走看看