• 【暑假集训】HZOI2019 水站 多种解法


    题目内容

    • 已知有一个$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

  • 相关阅读:
    v-cloak
    MVVM
    初识ajax
    装瓶学习法
    回调函数(call back)
    如何让学习变得纯粹?
    异步
    grep用法
    Shell中的&&与||的区别
    shell中使用>/dev/null 2>&1 丢弃信息
  • 原文地址:https://www.cnblogs.com/Midoria7/p/13324830.html
Copyright © 2020-2023  润新知