#树形dp#JZOJ 1087 鱼肉炸弹

题目

有n栋楼房,两栋楼房可以看见当且仅当中间的楼房不高于两栋楼房,使用炸弹可破坏某一栋楼房,问使用K枚炸弹使得能看见楼房i的楼房数的最大值最小。


分析

树形dp,容易得出
f [ x ] [ i + j ] = m i n ( f [ x ] [ i + j ] , m a x ( f [ l ] [ i ] , f [ r ] [ j ] ) + b [ x ] ) f[x][i+j]=min(f[x][i+j],max(f[l][i],f[r][j])+b[x]) f[x][i+j]=min(f[x][i+j],max(f[l][i],f[r][j])+b[x])
f [ x ] [ i + j + 1 ] = m i n ( f [ x ] [ i + j + 1 ] , m a x ( f [ l ] [ i ] , f [ r ] [ j ] ) ) f[x][i+j+1]=min(f[x][i+j+1],max(f[l][i],f[r][j])) f[x][i+j+1]=min(f[x][i+j+1],max(f[l][i],f[r][j]))


代码

#include <cstdio>
#include <cctype>
#include <cstring>
#define N 100000
using namespace std;
typedef long long ll;
int n,m,a[N+1],b[N+1],le[N+1],ri[N+1]; ll f[N+1][7];
int in(){
	int ans=0; char c=getchar();
	while (!isdigit(c)) c=getchar();
	while (isdigit(c)) ans=ans*10+c-48,c=getchar();
	return ans;
}
int build(int l,int r){
	if (l==r) return l;
	int s1=0,s;
	for (int i=l;i<=r;i++)
	if (s1<a[i]) s1=a[i],s=i;
	if (s>l) le[s]=build(l,s-1); //确定左边
	if (s<r) ri[s]=build(s+1,r);//确定右边
	return s;
}
ll min(ll a,ll b){return (a<b)?a:b;}
ll max(ll a,ll b){return (a>b)?a:b;}
void dp(int x){
	if (!x) return;
	int l=le[x],r=ri[x];
	dp(le[x]); dp(ri[x]);
	for (int i=0;i<=m;i++)
	for (int j=0;j<=m-i;j++){
		f[x][i+j]=min(f[x][i+j],max(f[l][i],f[r][j])+b[x]);//dp
		if (i+j<m) f[x][i+j+1]=min(f[x][i+j+1],max(f[l][i],f[r][j]));
	}
}
int main(){
	n=in(); m=in(); memset(f,127,sizeof(f));
	for (int i=1;i<=n;i++) a[i]=in(),b[i]=in();
	int root=build(1,n); f[0][0]=0; dp(root);
	return !printf("%lld",f[root][m]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值