1. 程式人生 > >[BZOJ4380][POI2015]Myjnie(區間 DP )

[BZOJ4380][POI2015]Myjnie(區間 DP )

Address

洛谷P3592
BZOJ4380

Solution

非常有意思的題。
先把 c i c_i 離散化。
定義狀態: f [

l ] [ r ] [ i ] f[l][r][i] 表示區間 [
l , r ] [l,r]
內的最小值為 i i ,從滿足 l
a b r l\le a\le b\le r
的人中獲得的最大收益。
邊界 f [ x ] [ x 1 ] [ i ] = 0 f[x][x-1][i]=0
轉移時列舉區間最小值所在的點 k k
易得,這時候左端點在 [ l , k ] [l,k] 內且右端點在 [ k , r ] [k,r] 內的區間的最小值位置都是 k k
統計出 c n t cnt 表示滿足 l a b r l\le a\le b\le r a k b a\le k\le b c i c\ge i 的人有多少個。
轉移就容易得出:
f [ l ] [ r ] [ i ] = max l k r { r e a l i × c n t + max h i f [ l ] [ k 1 ] [ [ h ] + max h i f [ k + 1 ] [ r ] [ h ] } f[l][r][i]=\max_{l\le k\le r}\{real_i\times cnt+\max_{h\ge i}f[l][k-1][[h]+\max_{h\ge i}f[k+1][r][h]\}
其中 r e a l i real_i 表示離散化前 i i 的實際值。
記錄下 f [ l ] [ r ] [ ] f[l][r][] 的字尾最大值,可以實現 O ( n 3 m ) O(n^3m) 的優秀複雜度。
輸出方案時只需要記錄 f [ l ] [ r ] [ i ] f[l][r][i] 從哪個 k k 轉移即可。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define For(i, a, b) for (i = a; i <= b; i++)
#define Rof(i, a, b) for (i = a; i >= b; i--)

inline int read()
{
	int res = 0; bool bo = 0; char c;
	while (((c = getchar()) < '0' || c > '9') && c != '-');
	if (c == '-') bo = 1; else res = c - 48;
	while ((c = getchar()) >= '0' && c <= '9')
		res = (res << 3) + (res << 1) + (c - 48);
	return bo ? ~res + 1 : res;
}

const int N = 55, M = 4005;

int n, m, tmpm, a[M], b[M], c[M], tmp[M], f[N][N][M], maxf[N][N][M],
cnt[M], mid[N][N][M], midf[N][N][M];

void addto(int l, int r, int mid)
{
	int i;
	For (i, 1, tmpm) cnt[i] = 0;
	For (i, 1, m)
		if (l <= a[i] && b[i] <= r && a[i] <= mid && mid <= b[i])
			cnt[c[i]]++;
}

void output(int l, int r, int x)
{
	int xmid = mid[l][r][x];
	if (l < xmid) output(l, xmid - 1, midf[l][xmid - 1][x]);
	printf("%d ", tmp[x]);
	if (xmid < r) output(xmid + 1, r, midf[xmid + 1][r][x]);
}

int main()
{
	int i, j, k, h;
	n = read(); m = read();
	For (i, 1, m) a[i] = read(), b[i] = read(), c[i] = tmp[i] = read();
	std::sort(tmp + 1, tmp + m + 1);
	tmpm = std::unique(tmp + 1, tmp + m + 1) - tmp - 1;
	For (i, 1, m)
		c[i] = std::lower_bound(tmp + 1, tmp + tmpm + 1, c[i]) - tmp;
	Rof (i, n, 1) For (j, i, n)
	{
		For (k, i, j)
		{
			addto(i, j, k);
			int sum = 0;
			Rof (h, tmpm, 1)
			{
				sum += cnt[h];
				int delta = maxf[i][k - 1][h] + maxf[k + 1][j][h] + tmp[h] * sum;
				if (delta > f[i][j][h] || !f[i][j][h])
					f[i][j][h] = delta, mid[i][j][h] = k;
			}
		}
		Rof (h, tmpm, 1)
			if (f[i][j][h] > maxf[i][j][h + 1] || !maxf[i][j][h + 1])
				maxf[i][j][h] = f[i][j][h], midf[i][j][h] = h;
			else maxf[i][j][h] = maxf[i][j][h + 1],
				midf[i][j][h] = midf[i][j][h + 1];
	}
	std::cout << maxf[1][n][1] << std::endl;
	output(1, n, midf[1][n][1]);
	std::cout << std::endl;
	return 0;
}