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

    题目

    LOJ2292

    分析

    比较神奇的一个区间 DP ,我看了很多题解都没看懂,大约是我比较菜罢。

    先明确一下题意:abcde 取完 c 后变成 abde ,可以取 bd 这样取 c 后新增的连续段。因此这题需要区间 DP。

    能发现取一段区间的代价只与这段区间的最大值和最小值有关。那么用 (f_{i,j,l,r}) 表示将区间 ([i,j]) 取到只剩下值在 ([l,r]) 中的数的最小代价,(g_{i,j}) 表示取完区间 ([i,j]) 的最小代价,则 (g_{1,n}) 就是答案。

    考虑怎么转移。对于一段区间而言,取区间末尾的那个数不会创造出新的连续段,也就是说不存在一步必须要取完末尾的数才能取到。因此末尾的数一定可以最后一步再取。那么大力枚举末尾的数是和前面多少个数一起取的,就有转移:

    [f_{i,j,l,r}=min_{k=i+1}^j f_{i,k-1,l,r}+g_{k,j} ]

    此外,如果末尾的数在 ([l,r]) 中,那么也可以不取。此时有转移:

    [f_{i,j,l,r}=f_{i,j-1,l,r} ]

    (g) 的转移就是枚举取最后一步时剩下的最大值和最小值,然后加上取这一次的代价。即:

    [g_{i,j}=min_{l=1}^{m}min_{r=l}^m f_{i,j,l,r}+a+b(r-l)^2 ]

    其中 (m) 是权值的最大值。

    离散化后时间复杂度 (O(n^5))

    代码

    #include <cstdio>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    using namespace std;
    
    namespace zyt
    {
    	const int N = 55, INF = 0x3f3f3f3f;
    	int n, a, b, f[N][N][N][N], g[N][N], arr[N], tmp[N];
    	int sq(const int x)
    	{
    		return x * x;
    	}
    	int work()
    	{
    		memset(f, INF, sizeof(f));
    		memset(g, INF, sizeof(g));
    		scanf("%d%d%d", &n, &a, &b);
    		for (int i = 1; i <= n; i++)
    			scanf("%d", &arr[i]), tmp[i] = arr[i];
    		sort(tmp + 1, tmp + n + 1);
    		int cnt = unique(tmp + 1, tmp + n + 1) - tmp - 1;
    		for (int i = 1; i <= n; i++)
    			arr[i] = lower_bound(tmp + 1, tmp + cnt + 1, arr[i]) - tmp;
    		for (int i = 1; i <= n; i++)
    			memset(f[i][i - 1], 0, sizeof(f[i][i - 1]));
    		for (int len = 1; len <= n; len++)
    			for (int i = 1; i + len - 1 <= n; i++)
    			{
    				int j = i + len - 1;
    				for (int l = 1; l <= cnt; l++)
    					for (int r = l; r <= cnt; r++)
    					{
    						if (l <= arr[j] && arr[j] <= r)
    							f[i][j][l][r] = min(f[i][j][l][r], f[i][j - 1][l][r]);
    						for (int k = i + 1; k <= j; k++)
    							f[i][j][l][r] = min(f[i][j][l][r], f[i][k - 1][l][r] + g[k][j]);
    					}
    				for (int l = 1; l <= cnt; l++)
    					for (int r = l; r <= cnt; r++)
    						g[i][j] = min(g[i][j], f[i][j][l][r] + a + b * sq(tmp[r] - tmp[l]));
    			}
    		printf("%d", g[1][n]);
    		return 0;
    	}
    }
    int main()
    {
    	return zyt::work();
    }
    
  • 相关阅读:
    实现SQL SERVER 下的PadLeft函数
    C#中String和string区别
    SQL经验分享(二)取得数据库中所有的表名、字段名以及字段属于哪个表
    JavaScript获取上传文件后缀名
    Url重写
    C#编程风格约定
    解决SharePoint 2003的爬网性能问题 之七
    [跨DB查询]查找SharePoint 2007中的Orphan Feature的SQL语句
    如何得到MOSS 2007的最近的100次爬网的信息
    解决SharePoint 2003的爬网性能问题 之八
  • 原文地址:https://www.cnblogs.com/zyt1253679098/p/12010468.html
Copyright © 2011-2022 走看看