P4766 [CERC2014]Outer space invaders——思维+区间dp

这篇博客介绍了如何运用线段树数据结构解决一个关于外星人的优化问题。通过将每个外星人转化为线段,并寻找最优化的策略来消除所有外星人,博主详细阐述了状态转移方程的建立过程,并提供了AC代码实现。文章重点讨论了如何通过离散化和区间划分来降低复杂度,最后给出了完整的C++代码示例。
摘要由CSDN通过智能技术生成

题目传送门

思路

这道题思维的转换真的很妙,第 i 个外星人出现的时间区间是 [ a i , b i ] [a_i,b_i] [ai,bi] ,距离为 d i d_i di ,
我们可以以时间区间为横坐标,距离区间为纵坐标考虑。
在这里插入图片描述

  • 观察上图,每个外星人可以转换为一条线段,我们要消灭这些外星人等价于竖着画一些线段,这些线段要碰到所有的外星人区间,求线段长度和最小为多少能消灭所有的外星人。
  • 对于一个时间区间 [ l , r ] [l,r] [l,r], 我们要消灭这里面的外星人,必须要在区间[l,r]里面里横坐标最远的那个小区间画一条线,即 d d d 最大的那个。然后再看左右子区间需要花费多少才能消灭这些外星人。
  • 所以我们得到状态转移方程 f [ l , r ] = m i n ( f [ l ] [ r ] , f [ l ] [ m − 1 ] + f [ m + 1 ] [ r ] + d ) f[l,r] = min(f[l][r], f[l][m-1]+f[m+1][r] +d) f[l,r]=min(f[l][r],f[l][m1]+f[m+1][r]+d), m表示在最远的那个子区间里面的哪个位置画一条线段, d d d 为最远的子区间里面的 d d d
  • 要注意离散化如果方法不当,可能会超时,也要注意数组不要开小了

AC代码

#include<bits/stdc++.h>
#define rep(i,x,y) for(int i=x; i<=y; ++i)
#define per(i,x,y) for(int i=x; i>=y; --i)
#define pushk push_back
#define emk emplace_back
#define popk pop_back
#define mem(a,b) memset(a,b,sizeof a)
#define PII pair<int,int>
#define ll long long
#define lp p<<1
#define rp p<<1|1
#define IOS ios::sync_stdio(fale),cin.tie(0)
#define INF 0x3f3f3f3f
using namespace std;
const int N = 330;
const int M = 1010;
int f[M][M]; 
int ff[M*10];
bool vis[M*10];
struct node{
	int a,b,d;
}nd[N];
int main() {

	int T;
	cin>>T;
	while(T--){
		int n;
		scanf("%d",&n);
		mem(vis,false);
		mem(f,0);
		rep(i,1,n){
			scanf("%d %d %d",&nd[i].a,&nd[i].b,&nd[i].d);
			vis[nd[i].a]=1,vis[nd[i].b]=1;
		}
		int tot=0;
		//去重离散化 
		rep(i,1,10001){
			if(vis[i]) ff[i]=++tot;
		}
		rep(i,1,n){
			nd[i].a=ff[nd[i].a],nd[i].b =ff[nd[i].b];
		} 
		//f[l][r]=min(f[l][r], f[l][m-1]+f[m+1][r]+d);
		//枚举区间长度 
		rep(len,1,tot){
			rep(j,1,tot-len+1){
				int l=j,r=j+len-1;
				int id=-1,d=0;
				rep(i,1,n){
					if(nd[i].a >=l && nd[i].b<=r){
						if(nd[i].d>d)  d=nd[i].d,id=i;	
					}
				}
				if(id==-1) continue;
				f[l][r]=INF;
				rep(i,nd[id].a,nd[id].b){
					f[l][r]=min(f[l][r], f[l][i-1]+f[i+1][r]+d);
				}
			} 
		}
		printf("%d\n",f[1][tot]);
	} 
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值