【解题报告】P4766 [CERC2014]Outer space invaders
这道题作为区间DP的补充还是非常有意思的。
一句话题意:
就是你知道每一个外星人的出生时间和消失时间和他和你的距离,为了消灭完他们!我们需要用一个很NB的武器,就是一个什么可以攻击一个圆的武器(以自己为圆心),每次消耗的能量为攻击半径,问我们消灭所有外星人消耗的最小的能量。
这道题首先我们应该想到的是首先先对所有的时间进行离散化,因为我们发现,只有最多300个外星人,但是时间的大小是10000,有一点大,但是只要我们离散化以后,开一个二维数组还是非常的合理的。
然后我们呢类似于合并石子的思路,我们定义当前二位动态规划数组的一维存的是从一个时间,二维存的是到另一个时间点,表示的是消灭此时间区间的外星人所需要的最小的能量。
然后我们就可以非常快速的推出动态转移方程式:
dp[s][e]=min(dp[s][e],dp[s][k-1]+a[id].v+dp[k+1][e]);
但是我们要明白的一点就是,我们这里肯定是要打到外星人,那么我们就必须确保这一段区间中存在外星人,并且一定是该区间距离中心最远的外星人。所以所我们每次枚举出来一个区间后一定要看该区间中有没有外星人存在并且要保证当前区间完全包含外星人,否则会出现混乱。然后我们就枚举这个k的时间点(k必须在我们枚举出的外星人存在范围内)然后进行dp取最小即可。
注意我们一定是从小区间开始然后慢慢合成到大区间!!!
所以说大家可以认真的思考一下代码中动态规划前面两个for循环为什么要这样写!
参考代码:
#include<bits/stdc++.h>
#define BIG 3000005
using namespace std;
struct sd{
int l,r,v;
}a[305];
int t,n;
int que[305*5],count_que=0,dp[2*305][2*305];
void manage()
{
count_que=0;
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].v);
que[++count_que]=a[i].l;
que[++count_que]=a[i].r;
}
sort(que+1,que+count_que+1);
count_que=unique(que+1,que+count_que+1)-que-1;
for(int i=1;i<=n;++i) a[i].l=lower_bound(que+1,que+1+count_que,a[i].l)-que,a[i].r=lower_bound(que+1,que+1+count_que,a[i].r)-que;//上面都是读入和离散化
//下面是真正的区间DP
for(int w=0;w<count_que;++w)
{
for(int s=1;s+w<=count_que;++s)
{
int e=s+w,id=-1;
for(int i=1;i<=n;++i)if(s<=a[i].l&&a[i].r<=e&&(id==-1||a[i].v>a[id].v))id=i;
if(id==-1){ dp[s][e] = 0;continue;}
dp[s][e]=BIG;
for(int k=a[id].l;k<=a[id].r;++k) dp[s][e]=min(dp[s][e],dp[s][k-1]+a[id].v+dp[k+1][e]);
}
}
printf("%d\n", dp[1][count_que]);
}
int main()
{
scanf("%d",&t);
for(int i=1;i<=t;++i) manage();
return 0;
}
By njc