【暑假集訓】HZOI2019 水站 多種解法
阿新 • • 發佈:2020-07-16
題目內容
- 已知有一個\(n\)層的水站:
- \(W_i\)表示未操作之前第\(i\)層的已有水量;
- \(L_i\)表示第\(i\)個水站能夠維持或者儲存的水的重量;
- 表示在第\(P_i\)層進行減壓放水操作所需的費用.
- 被壓減放水層所儲存的所有水都將流向下一層。如果第\(i\)層的水量比\(L_i\)大,則這一層也會(自動)減壓(不需要任何費用)。
- 現在想要使最後一層減壓(第\(n\)級),求最少的花費。這個任務現在交給了你。
輸入格式
- 每個輸入的第一行包含一個自然數\(n(1\le n\le 150000)\)。
- 接下來\(n\)行每行包含\(3\)個數\(W_i,L_i,P_i(0\le W_i,L_i,P_i\le 15000)\)
輸出格式
- 第一行輸出所需的最小費用。
- 第二行若干個整數,從小到大輸出必須減壓的層的編號。
程式碼
列舉+剪枝
#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\n",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\n",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(){
freopen("a.in","r",stdin);
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\n",ans);
sum = 0;
for(int i=num;i<=n;++i){
sum+=w[i];
if(sum<=l[i])printf("%d ",i);
}
return 0;
}
線段樹
參見林dalao程式碼,%%%。
學長:不要用資料結構