2008GDSOI 鱼肉炸弹【树形DP】

Time Limit:1000MS Memory Limit:65536K
Total Submit:24 Accepted:7


Description
  舒克和贝塔终于下定决心要去营救被关押在众猫聚居的 A A A城中的大米同志。
   A A A城的构造是很奇怪的。 A A A城中的所有 N N N栋建筑沿着一条直线排列,而且没有两栋楼的高度是相同的。而大米同志就被关押在其中的某栋建筑中。每一栋建筑的顶上都是有一些猫们在看守的。如果按照从一端到另一端的顺序将所有的建筑编号为1到 N N N,那么第i栋建筑的高度为Hi,顶上的猫的数量为 C i Ci Ci.
  每一只猫不但可以看守住其所在建筑的楼顶,还可以看守住一些比它所在建筑要低的楼的楼顶。前提是没有被其他楼所挡住。 A A A城中的建筑都是很高的,高到可以忽略它们之间的距离和它们的水平面面积。于是可以认为,楼i上的猫能看守楼 j j j的楼顶,当且仅当楼i的高度不低于楼 j j j,且楼 i i i到楼 j j j之间的所有楼房的高度都低于楼i。
  现在,神勇的贝塔同学已经潜入了 A A A城内部营救大米同志,而舒克则负责驾驶直升机提供空中支援。按照约定,贝塔找到并救出大米后会爬上楼顶施放信号让舒克前来接应。
  舒克的飞机上装备有 K K K枚鱼肉炸弹。每一枚鱼肉炸弹都可以在被投放到某一座楼的楼顶一段时间之后使该楼所有猫丧失行动能力。由于舒克并不知道贝塔会登上哪座楼的楼顶,所以现在他决定减少在最坏情况下与贝塔汇合时被发现的可能。具体来说,假设第i栋楼被 S i Si Si只猫看守(注意 S i Si Si只猫包括该楼上的 C i Ci Ci只猫以及其他楼上所有能看守该楼顶的猫),他希望使用这 K K K枚炸弹使得 S i Si Si中的最大值最小。聪明的舒克很快就通过正确地选择炸弹投放地点完成了一这目标。你能吗?


Input
第一行有两个整数 N N N K K K
第二行至第 N + 1 N+1 N+1行每行有两个整数,依次为编号为 1 1 1的楼到编号为 N N N的楼的高度 ( H i ) (Hi) (Hi)和楼顶的猫数( C i Ci Ci)。
数据规模:
1 < = N < = 100000 1<=N<=100000 1<=N<=100000
1 < = K < = 5 1<=K<=5 1<=K<=5
1 < = H i < = 1000000000 1<=Hi<=1000000000 1<=Hi<=1000000000
0 < = C i < = 1000000000 0<=Ci<=1000000000 0<=Ci<=1000000000

Output
在这个题目中,你只需输出一个整数,表示使用 K K 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
(说明:可投放在第四座楼上)


解题思路
建树的图示:
在这里插入图片描述

在这里插入图片描述

f [ d e p ] [ s ] f[dep][s] f[dep][s]表示在第 d e p dep dep栋楼代表的区间里用了 s s s个炸弹时这个区间最大的 s i s_i si
当不在这栋楼放炸弹时:
f [ d e p ] [ i + j ] = m i n ( f [ d e p ] [ i + j ] , m a x ( f [ l ] [ i ] , f [ r ] [ j ] ) + c [ d e p ] ) ; f[dep][i+j]=min(f[dep][i+j],max(f[l][i],f[r][j])+c[dep]); f[dep][i+j]=min(f[dep][i+j],max(f[l][i],f[r][j])+c[dep]);
当在这栋楼放炸弹时
f [ d e p ] [ i + j + 1 ] = m i n ( f [ d e p ] [ i + j + 1 ] , m a x ( f [ l ] [ i ] , f [ r ] [ j ] ) ) ; f[dep][i+j+1]=min(f[dep][i+j+1],max(f[l][i],f[r][j])); f[dep][i+j+1]=min(f[dep][i+j+1],max(f[l][i],f[r][j]));


代码

#include<algorithm>
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
using namespace std;
int n,m,root,h[101000],c[101000];
long long f[101000][7];
struct c{
	int l,r;
}a[101000];
int build(int l,int r){
	if(l==r) return l;
	int s=0,ss;
	for(int i=l;i<=r;i++)
	{
		if(h[i]>s)
		{
			s=h[i];
			ss=i;
		}
	}
	if(ss>l)
	a[ss].l=build(l,ss-1);
	if(ss<r)
	a[ss].r=build(ss+1,r);
	return ss;
}

void dp(int dep){
	if(!dep) return;
	int l=a[dep].l,r=a[dep].r;
	dp(l);
	dp(r);
	for(int i=0;i<=m;i++)
	{
		for(int j=0;j<=m-i;j++)
		{
			f[dep][i+j]=min(f[dep][i+j],max(f[l][i],f[r][j])+c[dep]);
			if(i+j<m)
				f[dep][i+j+1]=min(f[dep][i+j+1],max(f[l][i],f[r][j]));
		}
	}
}
int main(){
	scanf("%d%d",&n,&m);
	memset(f,127,sizeof(f));
	for(int i=1;i<=n;i++)
		scanf("%d%d",&h[i],&c[i]);
	root=build(1,n);
	f[0][0]=0;
	dp(root);
	printf("%lld",f[root][m]);
} 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值