洛谷 P5336 [THUSC2016]成绩单 区间DP

一个远古时期的坑终于填上了2333

我们设 \(f[l][r][x][y]\) 为使 \(l\)\(r\) 这段区间到达 值域 \(\in [x,y]\) 这个情况下的最小花费. \(g[l][r]\) 为将 \([l,r]\) 全都消去的最小花费

先枚举 \(l,r,x,y\)

\(f\) 的转移:
\(1\) .由 \([l,r-1]\) 添上 \(r\) 后转移

\(2\) .枚举断点 \(k\) ,由 \(f[l][k][x][y] + g[k + 1][r]\) 转移

\(g\) 的转移:

考虑将到达的f的局面消去,由 \(f[l][r][x][y] + a + b \times (y - x) ^2\)

最后答案 \(g[1][n]\) ,注意需要离散化。

#include<algorithm>
#include<cstring>
#include<iostream>
#include<cstdio>
using namespace std;
int n, m, a, b, len, l, r, x, y, k;
const int N = 60;
int h[N], lin[N], g[N][N], f[N][N][N][N];
int read()
{
	int res = 0; char ch = getchar(); bool XX = false;
	for (; !isdigit(ch); ch = getchar())(ch == '-') && (XX = true);
	for (; isdigit(ch); ch = getchar())res = (res << 3) + (res << 1) + (ch ^ 48);
	return XX ? -res : res;
}
void Min(int &a, int b) {if (a > b)a = b;}
int main()
{
	memset(f, 0x3f, sizeof(f)); memset(g, 0x3f, sizeof(g));
	cin >> n;
	cin >> a >> b;
	for (int i = 1; i <= n; ++i)h[i] = read(), lin[i] = h[i];
	sort(lin + 1, lin + 1 + n);
	m = unique(lin + 1, lin + 1 + n) - lin - 1;
	for (int i = 1; i <= n; ++i)
		h[i] = lower_bound(lin + 1, lin + 1 + m, h[i]) - lin, f[i][i][h[i]][h[i]] = 0, g[i][i] = a;
	for (len = 1; len <= n; ++len)
		for (l = 1; l <= n - len + 1; ++l)
		{
			r = l + len - 1;
			for (x = 1; x <= m; ++x)
				for (y = x; y <= m; ++y)
				{
					Min(f[l][r][min(x, h[r])][max(y, h[r])], f[l][r - 1][x][y]);
					for (k = l; k < r; ++k)
						Min(f[l][r][x][y], f[l][k][x][y] + g[k + 1][r]);
				}
			for (x = 1; x <= m; ++x)
				for (y = x; y <= m; ++y)
				{
					Min(g[l][r], f[l][r][x][y] + a + b * (lin[y] - lin[x]) * (lin[y] - lin[x]));
				}
		}
	cout << g[1][n];
	return 0;
}

updata 2020.6.14

之前的代码有点清奇,今天又考到了 所以来补一下坑

\(f[l][r][x][y]\)为将 \(l\)\(r\) 删的只剩下权值 \(x\)\(y\) 时最小花费, \(g[l][r]\) 为将 \(l\)\(r\) 删完的最小代价。

思路和之前一样,代码可能会更好理解一些。

\(f\) 的转移:
一:枚举断点
二:枚举删除的最右边的那个区间的左端点

\(g\) 的转移:

\(f[l][r][x][y] + a + b \times (y - x) ^2\) 虽然 \(l\)\(r\) 中可能没有 \(x\)\(y\) ,但这样显然不优,不会成为 \(g\)

#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
int n, a, b;
const int N = 52;
int val[N];
namespace solve2 
{
int tot;
int t[N], f[N][N][N][N], g[N][N];
int p2(int x) {return x * x;}
void work() 
{
    memset(f, 0x3f, sizeof(f)); memset(g, 0x3f, sizeof(g));
    for (int i = 1; i <= n; ++i)t[i] = val[i];
    sort(t + 1, t + 1 + n); tot = unique(t + 1, t + 1 + n) - t - 1;
    for (int i = 1; i <= n; ++i)val[i] = lower_bound(t + 1, t + 1 + tot, val[i]) - t;
    for (int i = 1; i <= n; ++i) 
    {
        for (int x = 1; x <= tot; ++x)
            for (int y = x; y <= tot; ++y) 
            {
                if (x <= val[i] && val[i] <= y)f[i][i][x][y] = 0;
                else f[i][i][x][y] = a;
            }
        g[i][i] = a;
    }
    for (int len = 2; len <= n; ++len) 
        for (int l = 1; l + len - 1 <= n; ++l) 
        {
            int r = l + len - 1;
            for (int x = 1; x <= tot; ++x)
                for (int y = x; y <= tot; ++y) 
                {
                    for (int k = l; k < r; ++k) 
                    {
                        f[l][r][x][y] = min(f[l][r][x][y]
                                      , min(f[l][k][x][y] + g[k + 1][r]
                                       ,    f[l][k][x][y] + f[k + 1][r][x][y]));
                    }
                }
            for (int x = 1; x <= tot; ++x)
                for (int y = x; y <= tot; ++y)
                    g[l][r] = min(g[l][r], f[l][r][x][y] + a + p2(t[y] - t[x]) * b);
        }
    cout << g[1][n];
}
}
int main() 
{
    cin >> n >> a >> b;
    for (int i = 1; i <= n; ++i)scanf("%d", &val[i]);
    solve2::work();
    return 0;
}
posted @ 2020-04-08 17:29  wljss  阅读(130)  评论(0编辑  收藏  举报