[POI2015]MYJ
[POI2015]MYJ
这道题就是告诉你有\(m\)个条件,满足其中的一些条件(即一个区间找到的最小值小于等于该数则累加该最小值到最终的答案),使得最终的答案最大。另外最大值给出具体方案。
那么,我们考虑DP做法:
维护一个\(dp[l,r,val]\)代表\([l,r]\)中最小值是\(val\)的最优解(最大值)。
有:
\[dp[l,r,val]=max(max(dp[l,pos-1,val~val_{limit}])+max(dp[pos+1,r,val~val_{limit}])+cnt[val,pos]*val)
\]
具体:就是枚举该区间所有可能的最小值,然后枚举最小值出现的位置(注意:如果一个位置是最小值了,那么其他位置只能是不少于最小值的数),对于所有在\([l,r]\)区间的条件,过该最小值且条件的权值不少于最小值,那么,该条件就累加到\(dp[l,r,val]\)贡献当中去。而我们还需要统计的是有\(cnt[val,pos]\)个满足条件的区间。
那么\(max(dp[l,pos-1,val,val_{limit}])\)怎么办?考虑到决策转移集合只增不减,我们考虑维护一个数组或变量优化。
所以,我们可以不妨预处理出所有的值:\(cnt[l,r,val,pos]\),指的是在区间\([l,r]\)中,最小值为\(val\),位置为\(pos\)(如果有多个位置满足则任选一个即可)条件的个数。
由于\(m\)很小,所以我们直接枚举即可。
最后按照方程递推。
具体方案咋办呢?
我们不妨记录每一个状态下去的最优值的最小值、和最小值所在位置,递归下去即可。
我们可以在统计\(dp[]\)过程中完成记录。
但是\(cnt[l,r,val,pos]\)空间开不下,那么只能现用现场计算啦!
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#define Re register
#define CLR(x, y) memset(x,y,sizeof(x))
#define FOR(i, x, y) for(Re int i=x;i<=y;++i)
#define ROF(i, x, y) for(Re int i=x;i>=y;--i)
using namespace std;
const int MAXN = 55, MAXM = 4000 + 10, INF = 1 << 30;
template <typename T> void read(T &x)
{
bool mark = false;
char ch = getchar();
for(; ch < '0' || ch > '9'; ch = getchar()) if(ch == '-') mark = true;
for(x = 0; ch >= '0' && ch <= '9'; ch = getchar()) x = (x << 3) + (x << 1) + ch - '0';
if(mark) x = -x;
return;
}
/*
dp[l, r, val] -> while the [l, r] minimum number is val, the optimal value(maximum) you can get
cnt[val, pos] -> calculate this while computing dp[l, r, val].
[l, r] the valid number in this interval.
f[l, r, val] -> max{dp[l, r, val ~ val_limit]}
vmax[l, r, val] -> the val which f[l, r, val] get the maximum number, core[l, r, val] -> the position where dp[l, r, val] get the best results
*/
int n, m, tot = 0, a[MAXM], b[MAXM], c[MAXM], table[MAXM];
int dp[MAXN][MAXN][MAXM] = {}, cnt[MAXM][MAXN] = {}, f[MAXN][MAXN][MAXM] = {};
int vmax[MAXN][MAXN][MAXM], core[MAXN][MAXN][MAXM];
void discrete()
{
sort(table + 1, table + m + 1);
tot = unique(table + 1, table + m + 1) - table - 1;
return;
}
int query(int x)
{
int L = 1, R = tot, mid;
while(L < R)
{
mid = L + ((R - L) >> 1);
if(table[mid] < x) L = mid + 1;
else R = mid;
}
return L;
}
void dfs(int l, int r, int k)
{
if(l > r) return;
int pos = core[l][r][k], L = pos - 1, R = pos + 1;
dfs(l, L, vmax[l][L][k]);
printf("%d ", table[k]);
dfs(R, r, vmax[R][r][k]);
return;
}
int main()
{
read(n), read(m);
FOR(i, 1, m)
{
read(a[i]), read(b[i]), read(c[i]);
table[i] = c[i];
}
discrete();
FOR(i, 1, m) c[i] = query(c[i]);
FOR(len, 1, n)
{
for(Re int l = 1, r = len; r <= n; ++ l, ++ r)
{
// computing dp[l, r, val]
// prework:
CLR(cnt, 0);
FOR(i, 1, m)
{
if(a[i] >= l && b[i] <= r)
FOR(j, a[i], b[i]) ++ cnt[c[i]][j];
}
ROF(i, tot - 1, 1)
FOR(j, l, r)
cnt[i][j] += cnt[i + 1][j];
// now display the process of working out the main issues
ROF(val, tot, 1)
{
int &ans = dp[l][r][val];
FOR(pos, l, r)
{
int can = f[l][pos - 1][val] + f[pos + 1][r][val] + cnt[val][pos] * table[val];//can -> candidate
if(can >= ans) ans = can, core[l][r][val] = pos;
}
if(ans < f[l][r][val + 1]) f[l][r][val] = f[l][r][val + 1], vmax[l][r][val] = vmax[l][r][val + 1];
else f[l][r][val] = ans, vmax[l][r][val] = val;
}
}
}
printf("%d\n", f[1][n][1]);
dfs(1, n, vmax[1][n][1]);
return 0;
}