2008GDSOI 鱼肉炸弹
Description
舒克和贝塔终于下定决心要去营救被关押在众猫聚居的A城中的大米同志。
A城的构造是很奇怪的。A城中的所有N栋建筑沿着一条直线排列,而且没有两栋楼的高度是相同的。而大米同志就被关押在其中的某栋建筑中。每一栋建筑的顶上都是有一些猫们在看守的。如果按照从一端到另一端的顺序将所有的建筑编号为1到N,那么第i栋建筑的高度为Hi,顶上的猫的数量为Ci.
每一只猫不但可以看守住其所在建筑的楼顶,还可以看守住一些比它所在建筑要低的楼的楼顶。前提是没有被其他楼所挡住。A城中的建筑都是很高的,高到可以忽略它们之间的距离和它们的水平面面积。于是可以认为,楼i上的猫能看守楼j的楼顶,当且仅当楼i的高度不低于楼j,且楼i到楼j之间的所有楼房的高度都低于楼i。
现在,神勇的贝塔同学已经潜入了A城内部营救大米同志,而舒克则负责驾驶直升机提供空中支援。按照约定,贝塔找到并救出大米后会爬上楼顶施放信号让舒克前来接应。
舒克的飞机上装备有K枚鱼肉炸弹。每一枚鱼肉炸弹都可以在被投放到某一座楼的楼顶一段时间之后使该楼所有猫丧失行动能力。由于舒克并不知道贝塔会登上哪座楼的楼顶,所以现在他决定减少在最坏情况下与贝塔汇合时被发现的可能。具体来说,假设第i栋楼被Si只猫看守(注意Si只猫包括该楼上的Ci只猫以及其他楼上所有能看守该楼顶的猫),他希望使用这K枚炸弹使得Si中的最大值最小。聪明的舒克很快就通过正确地选择炸弹投放地点完成了一这目标。你能吗?
Input
第一行有两个整数N和K。
第二行至第N+1行每行有两个整数,依次为编号为1的楼到编号为N的楼的高度(Hi)和楼顶的猫数(Ci)。
数据规模:
1<=N<=100000
1<=K<=5
1<=Hi<=1000000000
0<=Ci<=1000000000
Output
在这个题目中,你只需输出一个整数,表示使用K枚炸弹所能达到的Si中的最大值最小能是多少。
Sample Input
样例1
3 2
1 2
3 1
2 2
样例2
3 1
1 2
3 1
2 2
样例3
5 1
1 4
5 1
3 4
4 2
2 5
Sample Output
样例1
1
(说明:可投放在第一座楼和第三座楼上)
样例2
2
(说明:可投放在第二座楼上)
样例3
6
(说明:可投放在第四座楼上)
思路:
看到题目,可能大家觉得这根本就不是一个动态规划(树形dp),没关系,一起来慢慢推吧!
首先我们要知道s[i],表示能监视楼i的所有cat,不一定是这栋楼上的。
接着,对于n栋楼中,哪一栋能对n栋楼上的s[i]都有贡献呢?俗话说的好:站的高,看得远。没错,n栋中最高的楼一定对每栋楼都有贡献的。那大家是不是猜到了,我们把就把最高楼作为根,它左边为左子树,右边为右子树;
如样例一:
3 2
1 2
3 1
2 2
2(3,1)
/ \
1(1,2) 2(2,2)
//注:以前我从来不建树,要不是没要办法,这题也不想。(码风清奇,勿喷!!!)
ll build(ll l,ll r)
{
if(l>r) return 0;
if(l==r) return l;
ll root=0,val=0;
for(ll i=l;i<=r;i++) if(h[i]>val) val=h[i],root=i;
e[root].l=build(l,root-1);
e[root].r=build(root+1,r);
return root;
}
既然我们都推出如何建树了,那么再来证明一下为什么是树形dp吧!
假如一棵树要放k枚炸弹,是不是可以分为左边炸i个,右边炸k-i个;当然我们也可以左边炸i个,右边炸k-i-1个(为什么要减一,后面讲)。管他有几种不同的,是不是就是一个父子节点依赖子节点的关系,这不就是个树形dp了吗?
动态转移方程
根据上面说的,大家可能已经想到了……
设f[root][k]表示以root为根的子树,放k枚炸弹的最小最大值。
那我们是不是可以分为两种情况来讨论:
1.左子树放i枚炸弹,右子树放j枚炸弹,根不放 i+j<=k
2.左子树放i枚炸弹,右子树放j枚炸弹,根放一枚炸弹 i+j+1<=k
那方程不就欧了。
f[root][i+j]=min(f[root][i+j],max(f[e[root].l][i],f[e[root].r][j])+c[root]); //情况一
if(i+j+1<=k)
f[root][i+j+1]=min(f[root][i+j+1],max(f[e[root].l][i],f[e[root].r][j])); //情况二
code:
#include<cstdio>
#include<iostream>
#include<cmath>
#include<cstring>
#include<string>
#include<algorithm>
#include<vector>
#include<queue>
#define ll long long
#define fre(x) freopen(#x".in","r",stdin),freopen(#x".out","w",stdout);
using namespace std;
const int MAX=2147483647;
const int N=1e5+10;
struct node
{
ll l,r;
} e[N];
ll n,k,h[N],c[N],id,f[N][5];
bool vis[N];
ll build(ll l,ll r)
{
if(l>r) return 0;
if(l==r) return l;
ll root=0,val=0;
for(ll i=l;i<=r;i++) if(h[i]>val) val=h[i],root=i;
e[root].l=build(l,root-1);
e[root].r=build(root+1,r);
return root;
}
void tree_dp(ll root)
{
vis[root]=1;
if(!vis[e[root].l]) tree_dp(e[root].l);
if(!vis[e[root].r]) tree_dp(e[root].r);
for(ll i=0;i<=k;i++)
for(ll j=0;j<=k-i;j++)
{
f[root][i+j]=min(f[root][i+j],max(f[e[root].l][i],f[e[root].r][j])+c[root]);
if(i+j+1<=k)
f[root][i+j+1]=min(f[root][i+j+1],max(f[e[root].l][i],f[e[root].r][j]));
}
}
int main()
{
memset(f,127/3,sizeof(f));
f[0][0]=0;
scanf("%lld%lld",&n,&k);
for(ll i=1;i<=n;i++)
{
scanf("%lld%lld",&h[i],&c[i]);
}
id=build(1,n);
tree_dp(id);
printf("%lld",f[id][k]);
return 0;
}