5793: 飞扬的小鸟II
时间限制: 1 Sec 内存限制: 128 MB提交: 97 解决: 22
[ 提交][ 状态][ 讨论版]
题目描述
有n棵大树从左到右排成一排,编号为1到n,每棵有高度hi与疲劳值wi。 有一只鸟儿现在站在最左侧的1号大树上,它想飞到第n棵树上去,但它不 能连续飞行太远,当它在第i棵树上时,只能飞到第i + 1, i + 2,..., i + k棵 树上,并获得对应大树的疲劳值;同时,假如它飞到的那棵树的高度不小 于当前这棵树的高度,那么它会获得额外的d疲劳值。求飞到第n棵树的最 小疲劳值是多少。
输入
第一行三个整数n、k和d,由空格分割,表示树有多少棵,飞行的最大距离,以及额外的疲劳值。 接下来n行,第i行包含两个整数hi和wi,表示第i棵树的高度和疲劳值。
输出
输出一行,表示最小疲劳值。
样例输入
5 2 1
4 3
3 2
5 3
2 1
6 1
样例输出
8
提示
最优策略是1 → 2 → 4 → 5,总疲劳值是3 + 2 + 1 + 1,并在4 → 5时支付额外的1疲劳值。
对于20%的数据,n ≤ 1000;
对于另外40%的数据,d = 0;
对于100%的数据,1 ≤ k < n ≤ 3 ∗ 105, 0 ≤ hi ≤ 109, 0 ≤ wi ≤ 103, d ∈{0, 1}。
来源
正常看这个题目n*k稳得一批可以解,但是1S TL 没脾气
所以只能是考虑分解n或者k里面的一个 n肯定没得分解,k却可以,因为只要分析只要找到当前i-j<=k(i>j)中最小的那个就行,因为你想想没一个数肯定是从前边k个树中疲劳最小的那个过来是最舒服的,疲劳值是最小的,因为d只有0和1两种情况。
如果d==0无论如何肯定是找前K个最小的,如果d=1,当前找的第j是既然是最小的,南无想差肯定是>=1的吧,(如果是同样疲劳度的就按照树的高度进行排序,因为这样可以有效避免用到d这一种情况,也是最小的策略)。
用优先队列保存i-k-1 到 i 之间所有的可能,然后top的肯定是最小的而且高度是最高的那种,如果当前i找的最小的不在k的范围内,就pop找到合适一个为止。
好像这种方式叫做单调队列优化
long long dp[300055];
struct node{
int hi;//高度
int wi;//疲劳值
}N[300055];
struct Node{
int index;
int h;
int w;
friend bool operator < (Node a, Node b)
{
if(a.w==b.w)
return a.h<b.h;
return a.w>b.w;
}
};
priority_queue <Node> pr;
int main ()
{
int n,k,d;
scanf("%d%d%d",&n,&k,&d);
for(int i=1;i<=n;i++)
scanf("%d%d",&N[i].hi,&N[i].wi);
dp[1]=N[1].wi;
Node a;
a.h=N[1].hi;
a.w=N[1].wi;
a.index=1;
int i=2;
pr.push(a);
while(i<=n)
{
a=pr.top();
if(i-a.index<=k)
{
if(N[i].hi > N[a.index].hi)
dp[i]=dp[a.index]+N[i].wi+d;
else
dp[i]=dp[a.index]+N[i].wi;
a.index=i;
a.h=N[i].hi;
a.w=dp[i];
pr.push(a);
}
else
{
while(i-a.index>k)
{
pr.pop();
a=pr.top();
}
i--;
}
i++;
}
// for(int i=1;i<=n;i++)
printf("%lld\n",dp[n]);
return 0;
}