题目内容
- 已知有一个$n$层的水站:
- $W_i$表示未操作之前第$i$层的已有水量;
- $L_i$表示第$i$个水站能够维持或者储存的水的重量;
- 表示在第$P_i$层进行减压放水操作所需的费用.
- 被压减放水层所储存的所有水都将流向下一层。如果第$i$层的水量比$L_i$大,则这一层也会(自动)减压(不需要任何费用)。
- 现在想要使最后一层减压(第$n$级),求最少的花费。这个任务现在交给了你。
输入格式
- 每个输入的第一行包含一个自然数$n(1le nle 150000)$。
- 接下来$n$行每行包含$3$个数$W_i,L_i,P_i(0le W_i,L_i,P_ile 15000)$。
输出格式
- 第一行输出所需的最小费用。
- 第二行若干个整数,从小到大输出必须减压的层的编号。
样例输入
3 1000 1000 1 0 1000 2 2 10 100
样例输出
3 1 2
样例解释
给第一层和第二层减压即可。
代码
枚举+剪枝
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=150000+10;
int n,temp;
int w[maxn],l[maxn],p[maxn];
ll ans;
vector<int> res,mem;
void work(ll now){
mem.clear();
int x=now,tempw=w[now],tot=p[now],temp;
mem.push_back(now);
while(x<=n){
temp=w[++x]+tempw;
tempw=temp;
if(temp<=l[x]){
tot+=p[x];
mem.push_back(x);
}
if(tot>=ans)return;
}
if(tot<ans){
ans=tot;
res.clear();
for(int i=0;i<mem.size();i++){
res.push_back(mem[i]);
}
}
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&w[i],&l[i],&p[i]);
ans=p[n];res.push_back(n);
for(int i=1;i<n;i++)
work(i);
printf("%lld
",ans);
for(int i=0;i<res.size();i++)
printf("%d ",res[i]);
return 0;
}
二分+差分
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=150000+10;
const ll INF=1e15;
int n;
ll ans=INF;
int w[maxn],l[maxn],p[maxn];
int c[maxn],sum[maxn];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d%d",&w[i],&l[i],&p[i]);
sum[i]=sum[i-1]+w[i];
}
for(int i=1;i<=n;i++){
int x=lower_bound(sum,sum+i+1,sum[i]-l[i])-sum;
x++;
c[x]+=p[i];
c[i+1]-=p[i];
}
int temp;
for(int i=1;i<=n;i++){
c[i]=c[i-1]+c[i];
if(ans>c[i]){
ans=c[i];
temp=i;
}
}
printf("%lld
",ans);
int tot=0;
for(int i=temp;i<=n;i++){
tot+=w[i];
if(tot<=l[i])
printf("%d ",i);
}
return 0;
}
优先队列
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=150000+10;
const ll INF=1e15;
int n;
ll ans=INF;
int sum,num;
int w[maxn],l[maxn],p[maxn];
int s[maxn];
struct Node{
int sum,id;
bool operator < (const Node& x)const{
return x.sum<sum;
}
};
priority_queue<Node> q;
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d%d%d",&w[i],&l[i],&p[i]);
for(int i=n;i>=1;i--)
s[i]=s[i+1]+w[i];
for(int i=n;i>=1;i--){
sum+=p[i];
while(!q.empty()){
Node t=q.top();
if(s[i]<=t.sum)break;
sum-=p[t.id];
q.pop();
}
Node cur;
cur.id=i;
cur.sum=l[i]+s[i+1];
q.push(cur);
if(ans>sum){
ans=sum;
num=i;
}
}
printf("%lld
",ans);
sum = 0;
for(int i=num;i<=n;++i){
sum+=w[i];
if(sum<=l[i])printf("%d ",i);
}
return 0;
}
线段树
参见林dalao代码,%%%。
学长:不要用数据结构
(rvalue):不要无脑用数据结构
$rvalue$太巨辣我被爆锤辣QAQ