放假前就看过这题,一点思路也没-..看题解都没看懂...今终于A掉了...
一个水库分为N层,每层有一定量的水W,和一个承载上限L,以及爆破本层需要的花费.如果某层的水量超过了上限,或者是被炸掉了,这层的水就会落到下一层中(记为超载)。现在一些人想要使最下面一层既第N层超载,问在花费最少的情况下,需要爆破哪些层?
现在看题解还是没看懂...不过倒是受到了点启发。首先考虑一个n^2的做法,枚举水开始下落的起点,从这个点开始向下扫描一遍,如果某一层加上起点到这层之间水量的和足以超过上限,那么这层自然超载,否则就需要炸掉这层,并把花费加上。因为n是15000,这中做法显然会T掉,所以想办法优化..对于每一层,记Si=(Li-Sum(1,i)),对于每一层i,如果当前开始下落的起点为st的话,若Si+sum(1,st-1)<0)说明是可以自然超载的,否则就需要爆破。虽然对于每一层,是否可以自然超载取决于st,但是Si之间的大小关系却是有序的,所以考虑用一个小根堆(优先队列也行,更方便点),保存当前st下,需要爆破的各个层数。这样,从N开始向1枚举st,每次循环,首先检测堆顶的S+sum(1,st-1)是否小于0,不断删除堆顶元素直到S+sum(1,st-1)>=0此时堆中所有的元素都是需要爆破的..统计一下花费,枚举每个st时更新下花费的最小值和起点位置这题就OK了。
#include <iostream>
#include <cstdio>
#include <queue>
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn=25500;
int n,m;
int sum[maxn];
struct node
{
int w,l,s,cost;
node()
{
};
bool operator>(const node& b)const
{
return s<b.s;
}
bool operator<(const node& b)const
{
return s>b.s;
}
}a[maxn];
int p,q;
int main()
{
// freopen("in.txt","r",stdin);
scanf("%d",&n);
priority_queue<node> que;
for (int i=1; i<=n; i++)
{
scanf("%d%d%d",&a[i].w,&a[i].l,&a[i].cost);
sum[i]=sum[i-1]+a[i].w;
}
for (int i=1; i<=n; i++)
{
a[i].s=a[i].l-sum[i];
}
int ans=a[n].cost;
int st=n;
int fe=a[n].cost;
que.push(a[n]);
for (int i=n-1; i>=1; i--)
{
fe+=a[i].cost;
while (!que.empty())
{
node tp=que.top();
if (tp.s+sum[i-1]<0)
{
que.pop();
fe-=tp.cost;
}
else break;
}
if (fe<ans)
{
ans=fe;
st=i;
}
que.push(a[i]);
}
for (int i=st; i<=n; i++)
if (a[i].l>=sum[i]-sum[st-1])
{
printf("%d\n",i);
}
return 0;
}