【区间dp】bzoj 3928

传送门

参考博客

【题目大意】有若干个外星人,给定每个外星人出现的时间Ai~Bi和到你的距离Di,现在你有一个区域爆破器,可以消灭半径为R范围内所有的外星人,但是使用一次所需要的费用为R,使用次数无限制。问消灭所有外星人最小的费用是多少。

【思路】 如上图,可以把每个外星人看做一个有长度的线段。当你选择一个时间点开枪,一定会选择消灭该时刻距离你最远的一个,同时会消灭在该时刻存在的其他外星人【距离更近】。假设在T时刻开枪,那么现在就会剩下T时刻两边的外星人【结束时间<T,出现时间>T】然后就转化为了两个子问题。

dp[i][j]表示消灭【i<Ai<=Bi<j】的所有外星人的最小花费。

令k为开枪的时刻,那么转移就是:dp[i][j]=min(dp[i][j],dp[i][k-1]+dp[k+1][j]+D[k])

具体实现先把时间离散化。具体看代码把。

#include<bits/stdc++.h>
using namespace std;
const int maxn=500;
const int oo=1<<30;
int T,n,t[10050],cnt,dp[maxn][maxn];
struct alien{int st,ed,dis;}a[500];
inline int read(){
	int x=0;char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x;
}
void print(int x){
	if(x>9) print(x/10);
	putchar(x%10+48);
}
bool cmp(alien p1,alien p2){return p1.dis>p2.dis;}
inline void disc(){
	cnt=unique(t+1,t+cnt+1)-t-1;
	for(int i=1;i<=n;++i)
		a[i].st=lower_bound(t+1,t+cnt+1,a[i].st)-t,
		a[i].ed=lower_bound(t+1,t+cnt+1,a[i].ed)-t;
}
int DP(int l,int r){
	if(r<l) return 0;
	if(dp[l][r]!=-1) return dp[l][r];
	int pos=0;
	for(int i=1;i<=n;i++)
		if(a[i].st>=l&&a[i].ed<=r){pos=i;break;}
	if(!pos) return dp[l][r]=0;
	dp[l][r]=oo;
	for(int i=a[pos].st;i<=a[pos].ed;++i)
		dp[l][r]=min(dp[l][r],DP(l,i-1)+DP(i+1,r)+a[pos].dis);
	return dp[l][r];
}
int main(){
	T=read();
	while(T--){
		memset(dp,-1,sizeof(dp));
		n=read(),cnt=0;
		for(int i=1;i<=n;++i) t[++cnt]=a[i].st=read(),t[++cnt]=a[i].ed=read(),a[i].dis=read();
		sort(a+1,a+n+1,cmp),sort(t+1,t+cnt+1),disc();
		print(DP(1,cnt)),putchar('\n');
	}
}

猜你喜欢

转载自blog.csdn.net/g21wcr/article/details/83477608