题目链接:点击打开链接
题目大意:
给你一排不同种类的树,他们分别有自己的价值。让你给树分组,每组树的价值是这组树中的最高价值。分组有以下要求,
必须按照给定的顺序分组,一组树中不能有种类相同的树。求最后各组价值相加的最小值。
解题思路:
比赛的时候刚开始以为是贪心,不过想了几个贪心策略全部被干掉了。。。遂比赛结束也没有写出来这道题,赛后查了一下发现是线段树优化dp,惊了,还没做过线段树dp。。。
但是仔细想了这道题以后发现这tm明显是单调队列优化dp啊。
首先 用 type 数组和 h 数组存树的种类和价值。其次对于第 i 棵树,那么它可以被包含的组的最左边应该是 max(第(i-1)棵树的最左范围,跟 i 相同种类的树上一次出现的位置+1).知道了个这个范围,就可以在这个范围内愉快的进行dp了。转移方程就是
dp[ i ]=min(dp[ j-1 ]+max( h ( j , i ) ) ),这里就可以发现dp转移方程是符合单调队列优化的,因为可以在(j,i)范围内维护一个最优的 h[ que[head] ],这样的话时间上就优化了很多,题目也就解出来了。
以下贴代码
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <vector>
#include <queue>
#include <map>
#include <algorithm>
#include <stack>
#include <set>
#include <functional>
#define rank ra
#define next ne
#define pb push_back
#define hash haha
#define lson rt<<1
#define rson rt<<1|1
#define xlson xl,xmid,xt<<1
#define xrson xmid+1,xr,xt<<1|1
#define ylson yl,ymid,xt,yt<<1
#define yrson ymid+1,yr,xt,yt<<1|1
using namespace std;
typedef long long ll;
const int N=200005;
const int mod=999997;
int n;
int type[N],h[N]; //种类和价值
int que[N]; //单调队列
ll dp[N];
map<int,int> ma; //存每个种类的树最后出现的位置
int main()
{
int QAQ,kase=0;
scanf("%d",&QAQ);
while(QAQ--)
{
ma.clear();
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d",&type[i],&h[i]);
int l=0;
memset(dp,127,sizeof(dp)); //初始化dp数组
dp[0]=0;
int head=0,tail=-1;
for(int i=1;i<=n;i++)
{
l=max(l,ma[type[i]]); //选择范围左边界
ma[type[i]]=i; //更新该种类树的位置
while(head<=tail&&que[head]<=l) //不在范围内删掉
head++;
while(head<=tail&&h[que[tail]]<=h[i]) //维护单调性保证最优解
tail--;
que[++tail]=i; //加入队列
for(int j=head;j<=tail;j++) //dp转移
{
if(j!=head)
dp[i]=min(dp[i],dp[que[j-1]]+h[que[j]]);
else
dp[i]=min(dp[i],dp[l]+h[que[j]]);
}
}
printf("Case %d: %lld\n",++kase,dp[n]);
}
}